多线程
class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
d.start();
for(int i=1;i<1000;i++)
{
System.out.println("哈哈。。。。");
}
}
}
class Demo extends Thread
{
public void run()
{
for(int i=1;i<100;i++)
{
System.out.println("嘿嘿嘿嘿。。。。。。。。。。");
}
}
}
这是创建多线程的第一种方式:
继承Thread类。
步骤:
1,定义一个Thread类的子类。
2,复写Thread类中的run方法。将需要线程运行的代码存储到该run方法中。
run方法的唯一功能是:存储线程要运行的代码。
3,创建Thread类的子类对象。创建线程。
4,调用Thread类中的start方法开启线程并运行子类的run方法。
注意:开启线程时,是为了让线程去执行我们执行的代码,所以Thread类是给我们提供了一个线程要运行的代码存放的位置,但是要运行什么代码Thread类并不知道,所以我们需要将运行的代码存储到run方法里面。所以我们我们就沿袭父类中的方法,定义子类的特有方法即可,然后再调用覆盖父类的方法就好了。
在运行结果中我们可以看到:
原来每一个新建的线程都有自己的名称。
这个名称是通过 "Thread-编号" 形式体现的。
编号是从0开始的。
而且可以通过Thread类的getName方法获取到当前的线程对象的名称(Thread.currentThread().getName())。
主线程的名称就是: main
通过Thread.currentThread():获取到当前线程对象。
线程创建的第二种方式:
实现Runnable接口。
步骤:
1,定义一个Runnable接口的子类。
2,覆盖Runnable接口的run方法。
3,通过Thread类创建线程对象。
4,将实现Runnable接口的子类对象作为参数传递给Thread类的构造函数。
为什么要这么做呢?因为要告诉线程对象要运行哪个run方法。现在线程要执行的代码都存储到了Runnable接口的子类对象中。那么线程要运行这个run方法。必须要明确这个run方法所属的对象。所以将该对象传递给Thread构造函数。这样线程对象就可以去执行指定的run方法。
5,通过线程对象的start开启线程并执行。
这种方式的好处:避免了单继承的局限性。
所以通常情况下,创建线程,都使用实现Runnable接口的方式。
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println(Thread.currentThread().getName()+"....qqqqqq....."+x);
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();//创建一个对象,为了线面的线程实现资源共享
Thread t1 = new Thread(d);//创建线程对象,传入线程对象
Thread t2 = new Thread(d);//创建线程对象,传入线程对象
//开启两个线程
t1.start();
t2.start();
/*
创建了两个线程对象,都用start开启了,但是没有结果。
那是因为,start方法开启线程,并执行了线程的run方法。
*/
}
}
/*
//原始的Thread类
class Thread
{
private Runnable target;
Thread(Runnable target)
{
this.target = target;
}
public void run()
{
if(target!=null)
target.run();
}
}
*/
演示:线程的安全问题。
通过分析该程序,发现票有可能会出现错误的情况。
我们用sleep模拟了一下这个情况,可以验证该情况是存在的。
分析安全问题的原因:
四个线程在同时处理同一个资源票。
而且有两条语句在操作这个票数据。
当A线程对操作语句中的一部分执行完,还没有执行剩下的操作语句
就被B线程开始执行。如果B线程将数据改变。那么A线程在在执行时,
就会出现数据的错误。
简单说:多条操作共享数据的代码被多个线程分开执行导致的。
解决线程安全隐患的解决方案:
就是 同步代码块。
格式:
synchronized(对象)
{
需要被同步的代码;
}
同步代码块的对象可以是任意的对象。
同步的原理:
其实就是使用锁机制。
将多条操作共享资源的代码进行同步的封装,并加了锁。
只有获取到锁线程才可以进入到同步中,这时其他线程即使获取到执行权。
因为没有了锁,它们都进不来。这样就保证了数据的安全。解决了线程安全问题。
参考火车上的卫生间。
注意:什么代码需要定义到同步中呢?
只有操作了共享数据的多条代码才放到同步中。
同步的好处:解决了线程安全问题。
同步的弊端:对资源是一种耗费,相对降低效率。
同步的前提:
1,必须是两个或者两个以上的线程才需要同步。
2,多个线程必须使用同一个锁,才可以成为这些线程被同步了。
解决思想:在一个时间段,对于多条执行共享资源的语句,必须由一个线程执行完。
在执行过程中,其他线程不可以参与。
总结:
造成安全隐患的因素:
1,多个线程在操作共享资源。
2,有多条操作共享资源的代码。
*/
class Ticket implements Runnable
{
private int ticket = 100;
public void run()
{
while(true)
{
if(ticket>0)
{
try
{
Thread.sleep(10);
}
catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...sale .......:"+ticket--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步函数和同步代码块的区别?
同步函数书写较为简单。
同步代码块书写需要多一层缩进。
同步函数使用的锁是固定的 this。
同步代码块使用的锁可以是任意的。
在静态同步函数中的同步使用的锁是 类名.class
开发时,建议使用同步代码块。
多线程在单例设计中的应用
饿汉式:
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
懒汉式:
class Single
{
private static Single s = null;
private Single(){}
懒汉式:如果被多线程并发访问。会出现安全问题。不一定保证对象的唯一性。
所以加入了同步机制。但是会减低效率。
为了解决这个问题。
通过对s的双重判断搞定这个问题。减少判断锁的次数。
但是较饿汉式而言,代码相对复杂
所以建议单例使用饿汉式。
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
{
s = new Single();
}
}
}
return s;
}
public static synchronized Single getInstance2()
{
if(s==null)
{
s = new Single();
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
wait() 和 sleep() 的区别:
通过 执行权和锁这两个方面来分析区别。
sleep:释放执行权。不释放锁。同时自己定好时间自己醒来。
wait:释放执行权,释放锁。需要被别人叫醒。
/*
在jdk1.5以后。
java提供了更新的锁操作。
在java.util.concurent.locks包中提供了,新的对象 Lock接口。
这个对象的产生替换了synchronized同步。
好处在于:
synchronized对锁的操作是隐式。获取锁和释放锁,都是隐式的。
只要线程执行到同步语句就获取到锁,执行完同步语句,就释放锁。
用起来呢并不明显。
而Lock的出现,是按照面向对象的思想将锁操作封装成 对象。
并提供了对锁显示操作。lock():获取锁。unlock():释放锁。
操作监视器的方法wait notify notifyAll。都是需要关联自己所属的同步锁的。
有了新的锁对象以后,应该去找和这个新的锁对象关联的操作监视器的方法。
JDK1.5以后,在有了一个java.util.concurent.locks包中提供了一个Condition对象。
它用来替代Object。并提供了替代wait notify noitfyAll的方法。
wait-->await();
notify-->signal();
notifyAll-->signalAll();
并将这些操作监视器的方法封装到了Condition对象中。
如何让这个操作监视器的新对象和新的锁对象关联呢?
替换完代码以后功能和老代码是一样。
仅仅是用新的对象替换了老的程序部分。
但是signalAll出现还是一样会唤醒本方。效率一样低。
Lock的出现好处就是一个Lock对象上可以绑定多个监视器对象。
而以前同步的监视器方法只能绑定到一个锁上。
通过两组监视器,就可以完成生产者只唤醒消费。
消费者只唤醒生产者。
而不会出现本方唤醒本方的情况。
*/
import java.util.concurrent.locks.*;
class Resource
{
private String name;
private int count;
private boolean flag;
//创建一个锁对象。
private Lock lock = new ReentrantLock();//
//创建一个和该锁对象关联的监视器方法所属的Condition对象。
//private Condition con = lock.newCondition();
//在一个锁上提供两个监视器对象。一个负责生产者,一个负责消费。
private Condition proCon = lock.newCondition();
private Condition cusCon = lock.newCondition();
public void set(String name)
{
lock.lock();
try
{
while(flag)
//try{this.wait();}catch(Exception e){}。
try{proCon.await();}catch(Exception e){}//让生产者等待。
this.name = name+"-----"+count;
count++;
System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);
flag = true;
//this.notifyAll();
//con.signalAll();
cusCon.signal();//让生产者线程去唤醒消费者监视器上的线程。
}
finally
{
lock.unlock();//锁是一个资源。必须要释放。定义在finally中。
}
}
public void out()
{
lock.lock();
try
{
while(!flag)
// try{this.wait();}catch(Exception e){}
try{cusCon.await();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...........消费者......"+this.name);
flag = false;
//this.notifyAll();
//con.signalAll();
proCon.signal();
}
finally
{
lock.unlock();
}
}
}
class Producter implements Runnable
{
private Resource r;
Producter(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("产品");
}
}
}
class Customer implements Runnable
{
private Resource r;
Customer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProCusDemo3
{
public static void main(String[] args)
{
Resource r = new Resource();
Producter pro = new Producter(r);
Customer cus = new Customer(r);
//t1 t2 是生产者线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
//t3,t4是消费者线程。
Thread t3 = new Thread(cus);
Thread t4 = new Thread(cus);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
多线程
class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
d.start();
for(int i=1;i<1000;i++)
{
System.out.println("哈哈。。。。");
}
}
}
class Demo extends Thread
{
public void run()
{
for(int i=1;i<100;i++)
{
System.out.println("嘿嘿嘿嘿。。。。。。。。。。");
}
}
}
这是创建多线程的第一种方式:
继承Thread类。
步骤:
1,定义一个Thread类的子类。
2,复写Thread类中的run方法。将需要线程运行的代码存储到该run方法中。
run方法的唯一功能是:存储线程要运行的代码。
3,创建Thread类的子类对象。创建线程。
4,调用Thread类中的start方法开启线程并运行子类的run方法。
注意:开启线程时,是为了让线程去执行我们执行的代码,所以Thread类是给我们提供了一个线程要运行的代码存放的位置,但是要运行什么代码Thread类并不知道,所以我们需要将运行的代码存储到run方法里面。所以我们我们就沿袭父类中的方法,定义子类的特有方法即可,然后再调用覆盖父类的方法就好了。
在运行结果中我们可以看到:
原来每一个新建的线程都有自己的名称。
这个名称是通过 "Thread-编号" 形式体现的。
编号是从0开始的。
而且可以通过Thread类的getName方法获取到当前的线程对象的名称(Thread.currentThread().getName())。
主线程的名称就是: main
通过Thread.currentThread():获取到当前线程对象。
线程创建的第二种方式:
实现Runnable接口。
步骤:
1,定义一个Runnable接口的子类。
2,覆盖Runnable接口的run方法。
3,通过Thread类创建线程对象。
4,将实现Runnable接口的子类对象作为参数传递给Thread类的构造函数。
为什么要这么做呢?因为要告诉线程对象要运行哪个run方法。现在线程要执行的代码都存储到了Runnable接口的子类对象中。那么线程要运行这个run方法。必须要明确这个run方法所属的对象。所以将该对象传递给Thread构造函数。这样线程对象就可以去执行指定的run方法。
5,通过线程对象的start开启线程并执行。
这种方式的好处:避免了单继承的局限性。
所以通常情况下,创建线程,都使用实现Runnable接口的方式。
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println(Thread.currentThread().getName()+"....qqqqqq....."+x);
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();//创建一个对象,为了线面的线程实现资源共享
Thread t1 = new Thread(d);//创建线程对象,传入线程对象
Thread t2 = new Thread(d);//创建线程对象,传入线程对象
//开启两个线程
t1.start();
t2.start();
/*
创建了两个线程对象,都用start开启了,但是没有结果。
那是因为,start方法开启线程,并执行了线程的run方法。
*/
}
}
/*
//原始的Thread类
class Thread
{
private Runnable target;
Thread(Runnable target)
{
this.target = target;
}
public void run()
{
if(target!=null)
target.run();
}
}
*/
演示:线程的安全问题。
通过分析该程序,发现票有可能会出现错误的情况。
我们用sleep模拟了一下这个情况,可以验证该情况是存在的。
分析安全问题的原因:
四个线程在同时处理同一个资源票。
而且有两条语句在操作这个票数据。
当A线程对操作语句中的一部分执行完,还没有执行剩下的操作语句
就被B线程开始执行。如果B线程将数据改变。那么A线程在在执行时,
就会出现数据的错误。
简单说:多条操作共享数据的代码被多个线程分开执行导致的。
解决线程安全隐患的解决方案:
就是 同步代码块。
格式:
synchronized(对象)
{
需要被同步的代码;
}
同步代码块的对象可以是任意的对象。
同步的原理:
其实就是使用锁机制。
将多条操作共享资源的代码进行同步的封装,并加了锁。
只有获取到锁线程才可以进入到同步中,这时其他线程即使获取到执行权。
因为没有了锁,它们都进不来。这样就保证了数据的安全。解决了线程安全问题。
参考火车上的卫生间。
注意:什么代码需要定义到同步中呢?
只有操作了共享数据的多条代码才放到同步中。
同步的好处:解决了线程安全问题。
同步的弊端:对资源是一种耗费,相对降低效率。
同步的前提:
1,必须是两个或者两个以上的线程才需要同步。
2,多个线程必须使用同一个锁,才可以成为这些线程被同步了。
解决思想:在一个时间段,对于多条执行共享资源的语句,必须由一个线程执行完。
在执行过程中,其他线程不可以参与。
总结:
造成安全隐患的因素:
1,多个线程在操作共享资源。
2,有多条操作共享资源的代码。
*/
class Ticket implements Runnable
{
private int ticket = 100;
public void run()
{
while(true)
{
if(ticket>0)
{
try
{
Thread.sleep(10);
}
catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...sale .......:"+ticket--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步函数和同步代码块的区别?
同步函数书写较为简单。
同步代码块书写需要多一层缩进。
同步函数使用的锁是固定的 this。
同步代码块使用的锁可以是任意的。
在静态同步函数中的同步使用的锁是 类名.class
开发时,建议使用同步代码块。
多线程在单例设计中的应用
饿汉式:
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
懒汉式:
class Single
{
private static Single s = null;
private Single(){}
懒汉式:如果被多线程并发访问。会出现安全问题。不一定保证对象的唯一性。
所以加入了同步机制。但是会减低效率。
为了解决这个问题。
通过对s的双重判断搞定这个问题。减少判断锁的次数。
但是较饿汉式而言,代码相对复杂
所以建议单例使用饿汉式。
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
{
s = new Single();
}
}
}
return s;
}
public static synchronized Single getInstance2()
{
if(s==null)
{
s = new Single();
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
wait() 和 sleep() 的区别:
通过 执行权和锁这两个方面来分析区别。
sleep:释放执行权。不释放锁。同时自己定好时间自己醒来。
wait:释放执行权,释放锁。需要被别人叫醒。
/*
在jdk1.5以后。
java提供了更新的锁操作。
在java.util.concurent.locks包中提供了,新的对象 Lock接口。
这个对象的产生替换了synchronized同步。
好处在于:
synchronized对锁的操作是隐式。获取锁和释放锁,都是隐式的。
只要线程执行到同步语句就获取到锁,执行完同步语句,就释放锁。
用起来呢并不明显。
而Lock的出现,是按照面向对象的思想将锁操作封装成 对象。
并提供了对锁显示操作。lock():获取锁。unlock():释放锁。
操作监视器的方法wait notify notifyAll。都是需要关联自己所属的同步锁的。
有了新的锁对象以后,应该去找和这个新的锁对象关联的操作监视器的方法。
JDK1.5以后,在有了一个java.util.concurent.locks包中提供了一个Condition对象。
它用来替代Object。并提供了替代wait notify noitfyAll的方法。
wait-->await();
notify-->signal();
notifyAll-->signalAll();
并将这些操作监视器的方法封装到了Condition对象中。
如何让这个操作监视器的新对象和新的锁对象关联呢?
替换完代码以后功能和老代码是一样。
仅仅是用新的对象替换了老的程序部分。
但是signalAll出现还是一样会唤醒本方。效率一样低。
Lock的出现好处就是一个Lock对象上可以绑定多个监视器对象。
而以前同步的监视器方法只能绑定到一个锁上。
通过两组监视器,就可以完成生产者只唤醒消费。
消费者只唤醒生产者。
而不会出现本方唤醒本方的情况。
*/
import java.util.concurrent.locks.*;
class Resource
{
private String name;
private int count;
private boolean flag;
//创建一个锁对象。
private Lock lock = new ReentrantLock();//
//创建一个和该锁对象关联的监视器方法所属的Condition对象。
//private Condition con = lock.newCondition();
//在一个锁上提供两个监视器对象。一个负责生产者,一个负责消费。
private Condition proCon = lock.newCondition();
private Condition cusCon = lock.newCondition();
public void set(String name)
{
lock.lock();
try
{
while(flag)
//try{this.wait();}catch(Exception e){}。
try{proCon.await();}catch(Exception e){}//让生产者等待。
this.name = name+"-----"+count;
count++;
System.out.println(Thread.currentThread().getName()+".....生产者....."+this.name);
flag = true;
//this.notifyAll();
//con.signalAll();
cusCon.signal();//让生产者线程去唤醒消费者监视器上的线程。
}
finally
{
lock.unlock();//锁是一个资源。必须要释放。定义在finally中。
}
}
public void out()
{
lock.lock();
try
{
while(!flag)
//try{this.wait();}catch(Exception e){}
try{cusCon.await();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...........消费者......"+this.name);
flag = false;
//this.notifyAll();
//con.signalAll();
proCon.signal();
}
finally
{
lock.unlock();
}
}
}
class Producter implements Runnable
{
private Resource r;
Producter(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("产品");
}
}
}
class Customer implements Runnable
{
private Resource r;
Customer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProCusDemo3
{
public static void main(String[] args)
{
Resource r = new Resource();
Producter pro = new Producter(r);
Customer cus = new Customer(r);
//t1 t2 是生产者线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
//t3,t4是消费者线程。
Thread t3 = new Thread(cus);
Thread t4 = new Thread(cus);
t1.start();
t2.start();
t3.start();
t4.start();
}
}