Java Thread多线程
Java Thread多线程
Java 多线程例子1 小例子
public class ThreadDemo {
public static void main(String[] args) {
new TestThread().start();
while(true) {
System.out.println("main(): "+Thread.currentThread().getName() + " is running");
}
}
}
class TestThread extends Thread {
public TestThread(){
super("zhuyong");//设置线程的名字,默认为“TestThread”
}
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
输出结果为 写道
main(): main is running
TestThread: Thread-0 is running
main(): main is running
TestThread: Thread-0 is running
main(): main is running
TestThread: Thread-0 is running
Java 多线程例子2 前台线程(用户线程) 后台线程(守护线程 )
1,setDaemon(true)后就是后台线程(守护线程 ),反之就是前台线程(用户线程)
2,后台线程 和 前台线程的区别:在java程序中如果所有的前台线程都已经退出,所有的后台线程自动退出。
public class ThreadDemo {
public static void main(String[] args) {
Thread t = new TestThread();
t.setDaemon(true);
t.start();
}
}
class TestThread extends Thread {
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
运行当前台线程main退出后自动退出。去掉setDeamon,TestThread 就不会退出
Java 多线程例子3 联合线程 join()
a,直接联合:
public class ThreadDemo {
public static void main(String[] args) {
Thread t = new TestThread();
// t.setDaemon(true);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
while(true) {
System.out.println("main(): "+Thread.currentThread().getName() + " is running");
}
}
}
class TestThread extends Thread {
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
这种直接联合,2个线程就完全成了一个线程了。
b,条件联合:
public class ThreadDemo {
public static void main(String[] args) {
Thread t = new TestThread();
// t.setDaemon(true);
t.start();
int i=0;
while(true) {
System.out.println("main(): "+Thread.currentThread().getName() + " is running");
if(i++ ==10000) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class TestThread extends Thread {
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
当main线程中工作10000下后,把线程TestThread 联合进来,实际上就是main线程运行10000下后,一直等到TestThread 完成后,再运行。
c,联合10秒和分开:
public class ThreadDemo {
public static void main(String[] args) {
Thread t = new TestThread();
// t.setDaemon(true);
t.start();
int i=0;
while(true) {
System.out.println("main(): "+Thread.currentThread().getName() + " is running");
if(i++ ==10000) {
try {
t.join(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class TestThread extends Thread {
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
当main线程运行10000下后,与TestThread 联合10秒,其实就是让TestThread 运行10秒,然后在分别运行。
Java 多线程例子4 继承Thread 实现Runnable
class TestThread extends Thread {
public void run() {
while(true) {
System.out.println("TestThread: "+Thread.currentThread().getName() + " is running");
}
}
}
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int tickets = 100;
public void run() {
while(true) {
if(tickets<0)
break;
System.out.println(Thread.currentThread().getName() + " is saling " + tickets--);
}
}
}
这里模拟4个窗口,一起卖100张车票。新建一个线程可以通过继承Thread类或实现Runnable,几乎所有的线程实现都可以通过实现Runnable完成,而且该方法适合多个相同线程去处理同一个资源的情况,把虚拟的线程和数据有效分离,较好地体现了面向对象的设计思想。上面4个窗口一起卖100张票的例子有继承Thread的方法就不是太好实现。
Java 多线程例子5 实际例子的讨论
1, 网络聊天程序,如QQ。
发送信息和接受信息肯定要连个线程,你不可能自己输入发送信息的时候就不能接受对方的信息了。
2,图形界面程序。
要说多线程用的最多的恐怕就要数图形程序了,图形程序每隔一段时间就要刷新一次,要不然大家是看不到图形的。
3,www网络服务器。
不可能每个网页只能在一个时间内让一个访问,实际上,网络服务器会为每个访问者建立一个专属的线程。
其实,线程在计算机中的运行中本没有实现真正的并行运行,最终还是由cpu交替运行这些线程,所以我觉得如果说对于单CPU来说的话多线程提高了效率那是不对的。那么在什么样的情况下需要使用多线程呢,现在我先总结成,在一个程序中需要有多个事情长期存在,而且这些事情都会存在等待的情况,我们就可以采用多线程的技术来实现貌似这些事情都并行的程序了。
看上面举得例子是不是这种情况呢?
第一,网络聊天程序,讨论的是发送信息和接受信息这两件事,首先这两件事都是长期存在的,不可能发了一个信息就不发了,然后看是不是这两件事是不是都存在等待呢,发送不可能总在发送吧,同样对方也不可能总在发送这边就不可能总在接受了,两件事情都存在等待,发送是要等待用户输入信息,接受要等待对方发送信息。
第二,图形界面,比如一个图形界面中有两个按钮,就像两件事情,这两个按钮都长时间存在,而且都在等待用户的按键。
第三,网络服务器 多个用户长时间访问这个网页,网络服务器等待这些用户与服务器交互。
总结:需要用多线程 = 多任务共存长时间 + 多任务存在等待时间
Java 多线程例子6 线程安全 线程同步 同步代码块 同步函数
线程安全
出现线程安全就是在使用多线程的时候程序出现了不期望的结果。
怎样思考线程安全:线程中任何一步运行完都可能交出CPU的控制权。
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int tickets = 100;
public void run() {
while(true) {
if(tickets<=0)
break;
//try{Thread.sleep(10);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()
+ " is saling " + tickets--);
}
}
}
例子展示了四个售票员一起卖这100张票,那么卖出同样序号的票是我们不期望看到了情况吧,上面已经提到了,分析线程安全的最基本的方法就是认为线程运行的时候任何情况下都可以交出CPU的控制权,上面一共启动了4个线程,现在假设tickets的值为1时,线程1在运行完“if(tickets<=0)”后交出CPU,后线程2得到了CPU,卖出了票1,后线程1重新得到了CPU,又卖出了票1或0。当然,上述情况是我们假设在什么情况下会出现象这样的线程安全的问题,在一般情况,我们运行这个程序好几次也不能看出这样的线程安全问题,那我们怎么确定这个问题呢?我们可以用“sleep(100)”来主动交出CPU来验证我们的想法:去掉上面注释的sleep再运行程序,得到
运行结果 写道
Thread-1 is saling 2
Thread-2 is saling 1
Thread-3 is saling 0
Thread-0 is saling -1
Thread-1 is saling -2
卖出了0、-1和-2的票,证明上述这种写法是存在线程安全的。
线程同步
为了解决线程安全的问题,就要引入线程同步。
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int tickets = 100;
String str = ""; //此时str为公共对象
public void run() {
while(true) {
synchronized(str) {
if(tickets<=0)
break;
//try{Thread.sleep(10);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()
+ " is saling " + tickets--);
}
}
}
}
在上面的代码中加入synchronized,就可以实现线程同步了。
运行结果 写道
hread-2 is saling 5
hread-3 is saling 4
hread-1 is saling 3
hread-0 is saling 2
hread-2 is saling 1
这个线程同步怎么理解呢?这里有个锁旗标的概念,锁旗标可以理解为java中的每一个对象都有个标志位,该标志位开始的状态是1,当执行完synchronized后这个对象的标志位被置为了0,这个过程就说这个线程得到了这个对象的锁旗标,synchronized块运行完之后这个线程会让出这个对象的锁旗标,而每个线程在遇到synchronized是都回查看这个对象的锁旗标在不在,如果不在该线程就会主要让出CPU。
这里还要记住synchronized的对象一定要是多个线程的公共对象,要是各自的对象就不能实现同步了。如下面改变str定义的位置。
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int tickets = 100;
public void run() {
String str = "";//此时str为私有对象
while(true) {
synchronized(str) {
if(tickets<=0)
break;
//try{Thread.sleep(10);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()
+ " is saling " + tickets--);
}
}
}
}
结果为 写道
Thread-1 is saling 2
Thread-2 is saling 1
Thread-3 is saling 0
Thread-0 is saling -1
Thread-1 is saling -2
另外,在运行加入了synchronized同步块的程序的时会发现速度明显比没有同步块的程序要慢的多,所以在确定不会出现线程安全问题的程序中不要加入同步块,就像我们经常先使用Vector还是有ArrayList呢?它们两个的功能基本是完全一样的都是List,而Vector中的函数考虑了线程同步,ArrayList没有,这下就非常明显了,如果需要保证线程安全是就用Vector,不需要就用ArrayList效率高些。
同步函数
同步函数实现
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int tickets = 100;
public void run() {
while(true) {
sale();
}
}
public synchronized void sale() {
if(tickets<=0)
return;
try{Thread.sleep(10);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()
+ " is saling " + tickets--);
}
}
同步函数实际上同步的是this对象,这样如果要想某一个同步块和一个同步函数同步,就在同步块中使用this对象。
Java 多线程例子7 线程安全 死锁
死锁:在多个线程里对多个同步对象具有循环依赖时常会出现死锁。最简单的死锁例子就是线程一得到了A对象的锁旗标想得到B对象的锁旗标,线程二得到了B对象的锁旗标想得到A对象的锁旗标,这样线程一和线程二就形成了死锁。
class ThreadDemo {
public static void main(String[] args) {
TestThread t = new TestThread();
new Thread(t).start();
try{Thread.sleep(10);}catch(Exception e) {}
t.flag = true;
new Thread(t).start();
}
}
class TestThread implements Runnable {
boolean flag = false;
String A=new String("");
String B=new String("");
// String A="";
// String B="";
public void run() {
System.out.println(A==B);
if(flag) {
while(true) {
synchronized(A) {
try{Thread.sleep(100);}catch(Exception e) {}
synchronized(B) {}
}
System.out.println("AA...running...");
}
} else {
while(true) {
synchronized(B) {
try{Thread.sleep(100);}catch(Exception e) {}
synchronized(A) {}
}
System.out.println("BB...running...");
}
}
}
}
这里启动了两个线程,两个线程都循环想要得到A对象和B对象的锁旗标。
在试验这个例子的时候,还遇到了一个有意思的问题:刚开始我用的A和B对象是这么写的A="";B="";结果死活实验不出来死锁问题,这倒是挺奇怪的。结果最后发现原来这时候A和B引用了同一个对象,这是更java中对字符串常量的处理有关系,A="";时,java会在堆中建立一个字符串常量,这个字符串常量为空,由A引用,在B="",java发现字符串常量中有这么一个空字符串常量就不会新建了。
Java 多线程例子8 线程状态
在JDK的电子书中搜索Thread.State可以找到。
public static enum Thread.Stateextends Enum<Thread.State>线程状态。线程可以处于下列状态之一:
1.NEW
至今尚未启动的线程的状态。
2.RUNNABLE
可运行线程的线程状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。
3.BLOCKED
受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
4.WAITING
某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:
· 不带超时值的 Object.wait
· 不带超时值的 Thread.join
LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
5.TIMED_WAITING具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
· Thread.sleep
· 带有超时值的 Object.wait
· 带有超时值的 Thread.join
· LockSupport.parkNanos
· LockSupport.parkUntil
6.TERMINATED
已终止线程的线程状态。线程已经结束执行。
注意:在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
为了展现线程在运行时的状态及其转换,我画了下面这个图:
Java 多线程例子9 线程之间通信 wait notify notifyAll
程序实现了一个生产者和一个消费者,还有一个buffer用于存放生产出来的一个对象,buffer中只可以存放一个对象,buffer有一个标志位bFull,如果标志位为true表示buffer里有数值,如果bFull为false表示没有数值。buffer中的对象有两个属性,在多线程中如果不处理同步的话,可能出现属性不对应的情况。
wait:告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify为止。
notify:唤醒同一对象监视器中调用wait的第一线程。用于类似饭馆有一个空位子后通知所有等候就餐的顾客中的第一位可以入座的情况。
notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。
从上面明显可以看出wait、notify和notifyAll只能在synchronized方法中调用,记住一个对象的notify只能够唤醒被同一个对象wait的线程。
class ThreadDemo {
public static void main(String[] args) {
Buffer buf = new Buffer();
new Producer(buf).start();
new Consumer(buf).start();
}
}
class Producer extends Thread {
private Buffer buf;
public Producer(Buffer buf) {
this.buf = buf;
}
public void run() {
boolean odd = false;
while(true) {
synchronized(buf) {
if(buf.bFull)
try{buf.wait();}catch(Exception e) {}
if(odd) {
buf.name="jack";
try{Thread.sleep(100);}catch(Exception e) {}
buf.sex="female";
} else {
buf.name="lucy";
try{Thread.sleep(100);}catch(Exception e) {}
buf.sex="male";
}
buf.bFull = true;
buf.notify();
}
odd = !odd;
}
}
}
class Consumer extends Thread {
private Buffer buf;
public Consumer(Buffer buf) {
this.buf = buf;
}
public void run() {
while(true) {
synchronized(buf) {
if(!buf.bFull)
try{buf.wait();}catch(Exception e) {}
//try{Thread.sleep(100);}catch(Exception e) {}
System.out.println(buf.name + " : " + buf.sex);
buf.bFull = false;
buf.notify();
}
}
}
}
class Buffer {
boolean bFull = false;
String name = "Unkown";
String sex = "Unkown";
}
结果 写道
lucy : male
jack : female
lucy : male
jack : female
lucy : male
jack : female
面前对象 同步函数
class ThreadDemo {
public static void main(String[] args) {
Buffer buf = new Buffer();
new Producer(buf).start();
new Consumer(buf).start();
}
}
class Producer extends Thread {
private Buffer buf;
public Producer(Buffer buf) {
this.buf = buf;
}
public void run() {
boolean odd = false;
while(true) {
if(odd) {
buf.put("jack","female");
} else {
buf.put("lucy","male");
}
odd = !odd;
}
}
}
class Consumer extends Thread {
private Buffer buf;
public Consumer(Buffer buf) {
this.buf = buf;
}
public void run() {
while(true) {
buf.get();
}
}
}
class Buffer {
private boolean bFull = false;
private String name = "Unkown";
private String sex = "Unkown";
public synchronized void put(String name, String sex) {
if(bFull)
try{wait();}catch(Exception e) {}
this.name = name;
this.sex = sex;
bFull = true;
notify();
}
public synchronized void get() {
if(!bFull)
try{wait();}catch(Exception e) {}
System.out.println(name + " : " + sex);
bFull = false;
notify();
}
}
Java 多线程例子10 控制线程的生命 stop
在Thread类中stop已经不推荐大家使用了,因为使用stop停止的线程不安全,它并不会释放被该线程锁定的对象的锁旗标,这样其它线程如果也想要得到该对象的锁旗标就永远得不到了,形成死锁了。
利用标志位控制线程的生命周期:
public class threadtest {
public static void main(String[] args) {
Thread1 t = new Thread1();
t.start();
try{Thread.sleep(1);}catch(Exception e){};
for(int i=0; i<10; i++) {
System.out.println("main"
+ " is running.");
if(i==5)
t.stopMe();
}
}
}
class Thread1 extends Thread {
private boolean bStop = false;
public void stopMe() {
this.bStop = true;
}
public void run() {
while(!bStop) {
System.out.println(Thread.currentThread().getName()
+ " is running.");
}
}
}