深入浅出java多线程
一.多线程的第一种实现方式
继承Thread类
public class MyThread extends Thread {
public void run() {
for(int i=0;i<67;i++) {
System.out.println(getName()+" "+i);
}
}
}
二.多线程的第二种实现方式
实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<67;i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
//用Tread类来获取当前线程对象比较简单,直接用this就可以
//用Runnable接口就需要使用Tread.currentThread()
//继承方式,直接创建Thread子类对象
//实现Runnable接口创建的对象只能作为线程对象的target
}
}
三.Tread类中的一些常用方法
1.public final void join():等待该线程终止
为什么要用join()方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,
主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,
也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
public static void main(String[]args) {
Threadjoin m1=new Threadjoin();
Threadjoin m2=new Threadjoin();
m1.start();
try {
m1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
m2.start();
}
public void run() {
for(int i=0;i<67;i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getName()+" "+i);
}
}
2.yield
sleep()和yield()的区别
(1)sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;
yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到
可执行状态后马上又被执行。
(2)sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是
由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。
实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,
如有,则把 CPU 的占有权交给此线程,否则,继续运行原来的线程。
所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程
(3)sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,
当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。
在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,
那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
public void run() {
for(int i=1;i<88;i++) {
System.out.println(getName()+" "+i);
}
Thread.yield();
}
3.Deaaemon
public final void setDaemon(boolean on) :true时,表示为守护线程
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
守护线程不会立即结束掉,它会执行一段时间在结束掉)
该方法必须在启动线程前调用。
public static void main(String[]args) {
TreadDaemon t1=new TreadDaemon();
TreadDaemon t2=new TreadDaemon();
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
Thread.currentThread().setName("刘备");
for(int x =0 ; x < 5 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
三.模拟电影院卖票模式
1.
/**
* 模拟电影院卖票
* 实现多线程 方式一
* */
public class test {
public static void main(String[]args) {
ticket t1=new ticket();
ticket t2=new ticket();
ticket t3=new ticket();
//给各个线程起名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class ticket extends Thread{
private static int ticket=100;
public void run() {
while(true) {
if(ticket>0) {
System.out.println(getName()+"正在售出第"+(ticket--)+"张票");
}else {
System.exit(0);
}
}
}
}
2.
/**
* 模拟电影院卖票
* 实现多线程 方式二
* */
public class test {
public static void main(String[]args) {
seilTicket s=new seilTicket();
Thread t1=new Thread(s,"窗口1");
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class seilTicket implements Runnable {
private static int tickets=100;
@Override
public void run() {
while(true) {
if(tickets>0) {
System.out.println(Thread.currentThread().getName()+"正在售出第"
+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}
}
3. 模拟电影院卖票
加入睡眠,更符合实际情况,卖票系统会有延时
如果没有加入同步代码块,则会引起卖出负票,零票,以及一张票被卖出多次
这是由于CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
t1线程进来,睡完了,100张票
原子性操作:记录以前的值
接着tickets-- :票变成99张票
在马上输出99张票之前,t2/t3进来,直接输出记录的以前那个tickets的值
出现:
窗口1正在出售第100张票
窗口3正在出售第99张票
窗口2正在出售第99张票
原子性操作
引入同步代码块synchronized
public static void main(String[]args) {
seil s=new seil();
Thread t1=new Thread(s,"窗口1");
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
t1.start();
t2.start();
t3.start();
}
public class seil implements Runnable {
private static int tickets=100;
//同一个锁对象
private Object o=new Object();
@Override
public void run() {
while(true) {
//同步代码块
synchronized(o){//这就相当于一个线程执行到该对象的synchronized方法时,
//就为这个对象加上了一把锁,锁住了这个对象,
//别的线程在调用该方法时,发现了这把锁以后就继续等待下去了。
if(tickets>0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在售出第"+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}
}
}
synchronized关键字是一个修饰符,可以修饰方法或代码块,其的作用就是,对于同一个对象
(不是一个类的不同对象), 当多个线程都同时调用该方法或代码块时,必须依次执行,也就是说,
如果两个或两个以上的线程同时执行该段代码时,如果一个线程已经开始执行该段代码,
则另 外一个线程必须等待这个线程执行完这段代码才能开始执行。
4.静态同步方法
public class seil implements Runnable {
private static int tickets=100;
private Object o=new Object();
int x=0;
@Override
public void run() {
while(true) {
if(x%2==0) {
synchronized(seilTicket.class) {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在售出第"+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}else {
seilTickets();
}
x++;
}
}
//静态同步方法
public synchronized static void seilTickets() {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在售出第"+(tickets--)+"张票");
}else {
System.exit(0);
}
}
}
四.lock
public static void main(String[]args) {
seil s=new seil();
Thread t1=new Thread(s,"窗口1");
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
t1.start();
t2.start();
t3.start();
}
public class seil implements Runnable {
private static int tickets=100;
//创建Lock接口对象
private Lock lock=new ReentrantLock();
@Override
public void run() {
while(true) {
try {
lock.lock();//获取锁
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在售出第"+(tickets--)+"张票");
}else {
System.exit(0);
}
}finally {
if(lock!=null) {
//释放锁
lock.unlock();
}
}
}
}
}
五.消费者生产者模式
1.
public static void main(String[]args) {
student ss=new student();
setStudent s=new setStudent(ss);
getStudent g=new getStudent(ss);
Thread d1=new Thread(s);
Thread d2=new Thread(g);
d1.start();
d2.start();
}
public class setStudent implements Runnable {
private student s;
public setStudent(student s) {
super();
this.s = s;
}
@Override
public void run() {
s.name="卜凡";
s.age=22;
}
}
public class getStudent implements Runnable {
private student s;
public getStudent(student s) {
super();
this.s = s;
}
@Override
public void run() {
System.out.println(s.name+" "+s.age);
}
}
public class student {
String name;
int age;
}
2.
public static void main(String[]args) {
student ss=new student();
setStudent s=new setStudent(ss);
getStudent g=new getStudent(ss);
Thread d1=new Thread(s);
Thread d2=new Thread(g);
d1.start();
d2.start();
}
public class setStudent implements Runnable {
private student s;
public setStudent(student s) {
super();
this.s = s;
}
private int x=0;
/**
* 1)是否是多线程环境 是
2)是否有功共享数据 是
3)是否有多条语句对共享数据进行操作 有
同步机制(同步代码块/同步方法)
* */
@Override
public void run() {
while(true) {
synchronized(s) {//加入同步代码块
if(x%2==0) {
s.name="卜凡";
s.age=22;
}else {
s.name="王子异";
s.age=23;
}
x++;
}
}
}
}
public class getStudent implements Runnable {
private student s;
public getStudent(student s) {
super();
this.s = s;
}
@Override
public void run() {
while(true) {
System.out.println(s.name+" "+s.age);
}
}
}
public class student {
String name;
int age;
}
六.等待唤醒机制
public static void main(String[]args) {
student ss=new student();
setStudent s=new setStudent(ss);
getStudent g=new getStudent(ss);
Thread d1=new Thread(s);
Thread d2=new Thread(g);
d1.start();
d2.start();
//生产者线程
public class setStudent implements Runnable {
private student s;
public setStudent(student s) {
super();
this.s = s;
}
private int x=0;
@Override
public void run() {
while(true) {
synchronized(s) {
//资源类有数据,则生产者等待消费者消费,暂时不进行生产
if(s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//资源类没有数据了,则生产者进行生产
if(x%2==0) {
s.name="卜凡";
s.age=22;
}else {
s.name="王子异";
s.age=23;
}
x++;
//产生数据后,将flag的值改变为true
s.flag=true;
//通知消费者消费
s.notify();//唤醒后,t1,t2都互相等待
}
}
}
}
//消费者线程
public class getStudent implements Runnable {
private student s;
public getStudent(student s) {
super();
this.s = s;
}
@Override
public void run() {
while(true) {
synchronized(s) {
//资源类没有数据了
//消费者等待
if(!s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//资源类有数据,则消费者输出
System.out.println(s.name+" "+s.age);
//资源消费完了,将flag置为false
//通知生产者没有数据了,让生产者生产数据
s.flag=false;
s.notify();
}
}
}
}
}
//资源类
public class student {
String name;
int age;
boolean flag;
}
七.线程组
线程组是一个线程的集合,线程组也可以包含其他线程组
public static void main(String[]args) {
ThreadGroup tp=new ThreadGroup("新的线程组");
MyThread m=new MyThread();
Thread t1=new Thread(tp,m,"线程1");
Thread t2=new Thread(tp,m,"线程2");
t1.start();
t2.start();
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
}
public class MyThread implements Runnable{
@Override
public void run() {
for(int i=0;i<67;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
八.线程池
1.线程池
public static void main(String[]args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<97;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
2.future
private static Future<Integer> future;
public static void main(String[]args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//提交任务
Future<Integer> f1= pool.submit(new MyCallable(100));
Future<Integer> f2= pool.submit(new MyCallable(200));
//获取结果
Integer i1=f1.get();
Integer i2=f2.get();
System.out.println(i1);
System.out.println(i2);
//关闭线程池
pool.shutdown();
}
public class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
super();
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<num;i++) {
sum+=i;
}
return sum;
}
}
九.多线程的第三种实现方式
多线程实现方式第三种:
前提:自定义类实现Callable接口
1)创建线程池对象: Executors 里面的那个方法,返回的是ExecutorsService
2) 然后调用ExecutorsService里面的提交任务的方法:
<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行
3)关闭线程池
public static void main(String[]args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//调用提交方法
pool.submit(new MyCallable());
pool.submit(new MyCallable());
pool.shutdown();
}
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for(int i=0;i<97;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
return null;
}
}
上一篇: QT 加载歌词LRC文件