多线程
一、线程通信

1.这些方法存在同步中,因为要对监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才有锁的概念。
2.使用这些方法时,必须要标识所属的同步的锁。只有同一个锁上的被等待线程,可以被同一个锁上的notify()唤醒,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁。
3.锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中。

wait():释放资源,释放锁。
sleep():释放资源,不释放锁。
线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。
例:
class Res
{
String name;
String sex;
}
class Input implements Runnable
{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(r){
if(x==0){
r.name = "mike";
r.sex = "man";
}
else{
r.name = "丽丽";
r.sex = "女女女女女";
}
x = (x+1)%2;
}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
synchronized(r){
System.out.println(r.name+"..."+r.sex);
}
}
}
}
class Demo
{
public static void main(String []args){
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
发现:程序有待优化,程序在疯狂打印,而不是输入一个打印一个的效果。
优化:采用wait()和notify()控制输入和输出。
class Res
{
private String name;
private String sex;
private boolean flag = false;
Res(){}
public synchronized void set(String name,String sex){
this.name = name;
this.sex = sex;
flag = true;
this.notify();
if(flag){
try{this.wait();}catch(Exception e){}
}
}
public synchronized void get(){
System.out.println(name+"..."+sex);
flag = false;
this.notify();
if(!flag){
try{this.wait();}catch(Exception e){}
}
}
}
class Input implements Runnable
{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x =0;
while(true){
if(x==0){
r.set("mike","man");
}
else{
r.set("丽丽","女女女女");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
r.get();
}
}
}
class Demo
{
public static void main(String []args){
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
需求:生产者与消费者
class Product
{
private String name;
private int count;
private boolean flag = false;
Product(){}
public synchronized void set(String name){
if(flag){
try{this.wait();}catch(Exception e){}
}
this.name = name+count++;
System.out.println(Thread.currentThread().getName()+"生产------"+this.name);
flag = true;
this.notify();
}
public synchronized void get(){
if(!flag){
try{this.wait();}catch(Exception e){}
}
System.out.println(Thread.currentThread().getName()+"消费---------------"+this.name);
flag= false;
this.notify();
}
}
class SC implements Runnable
{
private Product pd;
SC(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.set("+商品+");
}
}
}
class XS implements Runnable
{
private Product pd;
XS(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.get();
}
}
}
class Demo
{
public static void main(String []args){
Product pd = new Product();
SC sc = new SC(pd);
XS xs = new XS(pd);
Thread t1 = new Thread(sc);
Thread t2 = new Thread(xs);
t1.start();
t2.start();
}
}
发现:只有一个生产和一个消费,想要多个线程同时执行,已提高效率。所以对程序改进。
class Product
{
private String name;
private int count;
private boolean flag = false;
Product(){}
public synchronized void set(String name){
if(flag){
try{this.wait();}catch(Exception e){}
}
this.name = name+count++;
System.out.println(Thread.currentThread().getName()+"生产------"+this.name);
flag = true;
this.notify();
}
public synchronized void get(){
if(!flag){
try{this.wait();}catch(Exception e){}
}
System.out.println(Thread.currentThread().getName()+"消费---------------"+this.name);
flag= false;
this.notify();
}
}
class SC implements Runnable
{
private Product pd;
SC(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.set("+商品+");
}
}
}
class XS implements Runnable
{
private Product pd;
XS(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.get();
}
}
}
class Demo
{
public static void main(String []args){
Product pd = new Product();
SC sc = new SC(pd);
XS xs = new XS(pd);
Thread t1 = new Thread(sc);
Thread t2 = new Thread(sc);
Thread t3 = new Thread(xs);
Thread t4 = new Thread(xs);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
问题分析:程序出现了上产两个,而消费一个的情况。t1执行完后,t2抢到运行资格但wait()了,这时t3执行完notify(),然后t1又抢到资格执行notify(),t2醒来接着执行,所以就发生生产两个消费一个的情况。
解决思路:让每次wait()醒来的线程进行flag判断。因为if只判断一次,所以要换成while。
问题分析:所有线程wait了。
解决思路:这时候,因为已经没有线程在运行了,都在冻结状态,所以使用notifyAll。
class Product
{
private String name;
private int count;
private boolean flag = false;
Product(){}
public synchronized void set(String name){
while(flag){
try{this.wait();}catch(Exception e){}
}
this.name = name+count++;
System.out.println(Thread.currentThread().getName()+"生产------"+this.name);
flag = true;
this.notifyAll();
}
public synchronized void get(){
while(!flag){
try{this.wait();}catch(Exception e){}
}
System.out.println(Thread.currentThread().getName()+"消费---------------"+this.name);
flag= false;
this.notifyAll();
}
}
class SC implements Runnable
{
private Product pd;
SC(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.set("+商品+");
}
}
}
class XS implements Runnable
{
private Product pd;
XS(Product pd){
this.pd = pd;
}
public void run(){
while(true){
pd.get();
}
}
}
class Demo
{
public static void main(String []args){
Product pd = new Product();
SC sc = new SC(pd);
XS xs = new XS(pd);
Thread t1 = new Thread(sc);
Thread t2 = new Thread(sc);
Thread t3 = new Thread(xs);
Thread t4 = new Thread(xs);
t1.start();
t2.start();
t3.start();
t4.start();
}
}

1.定义循环结束标记。因为线程运行代码一般都是循环的,只要控制了循环即可。
2.使用interrupt();中断方法。该方法是结束线程的冻结状态,使线程回到运行状态中来。
注:stop();方法已经过时不再使用。
例:
class Test implements Runnable
{
private boolean flag = true;//定义结束标记
Test(){}s
public void run(){
while(flag){//当条件不满足时,线程就结束了
System.out.println("hahahahaha");
}
}
public void setFlag(){
flag = false;
}
}
class Demo
{
public static void main(String []args){
Test test = new Test();
Thread t = new Thread(test);
t.start();
for(int x=0;x<100;x++){
System.out.println("main"+"-----"+x);
}
test.setFlag();
}
}
例:
class Test implements Runnable
{
private boolean flag = true;
public synchronized void run(){
try{this.wait();}catch(Exception e){}
System.out.println("over");
}
}
class Demo
{
public static void main(String []args){
Test test = new Test();
Thread t = new Thread(test);
t.start();
try{Thread.sleep(5000);}catch(Exception e){}
t.interrupt();
}
}
特殊情况:
class Test implements Runnable
{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
this.wait();
}
catch(InterruptedException e){
System.out.println("throw Exception");
}
}
System.out.println("run");
}
public void change(){
flag = false;
}
}
class Demo
{
public static void main(String []args){
Test test = new Test();
Thread t = new Thread(test);
t.start();
try{Thread.sleep(2000);}catch(Exception e){}
test.change();
}
}
发现:如果线程冻结了,即使改变了标记,也停止不了线程。
解决办法:把线程中断,从冻结状态变成运行状态,再对标记进行改变,就能停掉线程。
class Test implements Runnable
{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
this.wait();
}
catch(InterruptedException e){
System.out.println("throw Exception");
change();
}
}
System.out.println("run");
}
public void change(){
flag = false;
}
}
class Demo
{
public static void main(String []args){
Test test = new Test();
Thread t = new Thread(test);
t.start();
try{Thread.sleep(2000);}catch(Exception e){}
t.interrupt();
}
}
总结:当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结状态进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。Thread类中提供了该方法:interrupt();。
守护线程:setDaemon(boolean on);将线程标记为守护线程或用户线程。需要在启动线程前调用,也就是start();前。
特点:后台线程开启后和前台线程共同抢夺CPU执行权运行。当前台线程全部结束,后台线程会自动结束。
例:
class Demo
{
public static void main(String []args){
Thread t = new Thread(){
public synchronized void run(){
try{this.wait();}catch(Exception e){}
}
};
t.setDaemon(true);
t.start();
for(int x=0;x<60;x++){
System.out.println(x);
}
}
}
分析:虽然开启的线程是冻结状态,但是由于是守护线程,所以当主线程结束,守护线程也自动结束了。
join():等待该线程终止。
特点:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程执行完,A才会执行,join可以用来临时加入线程执行。
例:
class Demo implements Runnable
{
public void run(){
for(int x=0;x<70;x++){
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}
class JoinDemo
{
public static void main(String []args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
try{t1.join();}catch(Exception e){}
t2.start();
for(int x=0;x<80;x++){
System.out.println("main"+"..."+x);
}
System.out.println("over");
}
}
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
名称:Thread-1。
优先级:5。
线程组:main。
setPriority(int newPriority):更改线程的优先级。默认优先级为5,总共10级,1~10;
MAX_PRIORITY:线程可以具有的最高优先级。
MIN_PRIORITY:线程可以具有的最低优先级。
NORM_PRIORITY:分配给线程的默认优先级。
yield():暂停当前正在执行的线程对象,并执行其他线程。让线程都有机会执行。