2.线程:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
3.单线程和多线程的区别
public class TestThread{
public static void main(String[] args) throws InterruptedException {
//主线程
System.out.println("主线程启动。。。。");
Thread thread = new Thread(new ChildThread()); // 创建子线程
thread.start(); //子线程开始运行
System.out.println("主线程结束。。。。");
}
}
class ChildThread implements Runnable{
//重写子线程的run方法
@Override
public void run() {
try {
//异常处理,括号内的为可能会出错的代码
System.out.println("子线程启动。。。。");
Thread.sleep(5000);
System.out.println("子线程结束。。。。");
} catch (InterruptedException e) {
//(异常类型 异常对象引用)
e.printStackTrace(); //用于处理异常的代码
}
}
}
2.情况2:主线程开启了子线程,但是主线程结束,子线程也随之结束(守护线程)
public class TestThread{
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程启动。。。。");
Thread thread = new Thread(new ChildThread());
//thread.setDaemon(true);
thread.start();
System.out.println("主线程结束。。。。");
}
}
class ChildThread implements Runnable{
@Override
public void run() {
try {
System.out.println("子线程启动。。。。");
ThirdThread thiredThread = new ThirdThread(); //创建孙子线程
thiredThread.setDaemon(true);//将孙子线程(thiredThread)设为子线程(ChildThread)的守护线程,所以子线程结束,孙子线程无论是否运行完都会结束
/*!!!对于运行中的线程,不能设置为守护线程。不然会抛出java.lang.IllegalThreadStateException异常
*/
thiredThread.start();
Thread.sleep(1000);
System.out.println("子线程结束。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThirdThread extends Thread{
@Override
public void run() {
System.out.println("孙子线程启动。。。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("孙子线程结束。。。。");
}
}
3.情况3:主线程开了一个子线程,主线程必须等子线程结束才能结束
public class TestThread{
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程启动。。。。");
Thread thread = new Thread(new ChildThread());
thread.start();
thread.join(); //线程阻塞,让主线程等子线程结束,才继续执行
System.out.println("主线程结束。。。。");
}
}
class ChildThread implements Runnable{
@Override
public void run() {
try {
System.out.println("子线程启动。。。。");
Thread.sleep(5000);
System.out.println("子线程结束。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结:对于CPU来说,不存在主线程和子线程之分,线程是CPU调度的基本单位。
setDaemon 和 join改变了主线程和子线程的关系,实际应该是JVM接口代码做了处理干扰了线程的生命周期。
守护线程和非守护线程本质上没区别,但是虚拟机如果都是守护线程,虚拟机会退出,只要虚拟机还剩一个非守护线程,虚拟机都不会退出。
package cn.itcats.thread.Test1;
//方式1:继承Thread,重写run方法
public class Demo1 extends Thread{
//Demo1类继承Thread
//重写的是父类Thread的run()
public void run() {
System.out.println(getName()+"is running...");
}
public static void main(String[] args) {
Demo1 demo1 = new Demo1(); //创建线程
Demo1 demo2 = new Demo1();
demo1.start(); //线程启动
demo2.start();
}
}
package cn.itcats.thread.Test1;
//实现Runnable接口,重写run()
//实现Runnable接口只是完成了线程任务的编写,启动线程,需要new Thread(Runnable target),再调用thread对象调用start()启动
//使用面向接口,将任务与线程分离,有利于解耦
public class Demo2 implements Runnable{
//实现Runnable接口
public void run() {
System.out.println("implements Runnable is running");//重写run()
}
public static void main(String[] args) {
Thread thread1 = new Thread(new Demo2());//创建线程
Thread thread2 = new Thread(new Demo2());
thread1.start();//开启线程
thread2.start();
}
}
package cn.itcats.thread.Test1;
//方式3匿名内部类,创建启动线程次数较少的情况,书写更便捷
public class Demo3 {
public static void main(String[] args) {
//方式1:相当于继承了Thread类,作为子类重写run()实现,Demo1的缩减版
new Thread() {
public void run() {
System.out.println("匿名内部类创建线程方式1。。。");
}
}.start();
//方式2:实现Runnable,Runnable作为内部类,Demo2的缩减版
new Thread(new Runnable() {
public void run() {
System.out.println("匿名内部类创建线程方式2...");
}
}).start();//我的理解是直接在类里面创建线程直接启动
}
}
package cn.itcats.thread.Test1;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
/**
* 方式4:实现Callable 接口
* 含返回值且可抛出异常的线程创建启动方式
*/
public class Demo5 implements Callable<String>{
//实现Callable接口
public String call() throws Exception {
System.out.println("正在执行新建线程任务");
Thread.sleep(2000);
return "新建线程睡了2s后返回执行结果"; //返回值
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Demo5 d = new Demo5();
/* call()只是线程任务,对线程任务进行封装
class FutureTask implements RunnableFuture
interface RunnableFuture extends Runnable, Future
*/
//Callable接口将task传给Futuretask
FutureTask<String> task = new FutureTask<>(d);
//建立一个Thread对象调用start,启动
Thread t = new Thread(task);
t.start();
System.out.println("提前完成任务...");
//获取任务执行后返回的结果,可以外部异步获得子线程的运行结果
String result = task.get();
System.out.println("线程执行结果为"+result);
}
}
package cn.itcats.thread.Test1;
import java.util.Timer;
import java.util.TimerTask;
//创建启动线程之Timer定时任务,使用方法与Demo3相似
public class Demo6 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
System.out.println("定时任务延迟1,每隔1000ms执行一次");
}
},1,1000);
}
}
//当任务执行完毕或想执行不同任务时,实现起来比较麻烦。框架“quartz”
package cn.itcats.thread.Test1;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class Demo7 {
public static void main(String[] args) {
//创建带有10个线程的线程池,若创建的是CachedThreadPool则不需要指定线程数量,线程数量取决于线程任务,不够用则创建线程,够用则回收
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++) {
threadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"is running");
}
});
}
threadPool.shutdown();//销毁线程池,如若不销毁,会出现运行完毕,但程序并未停止
}
}
package cn.itcats.thread.Test1;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2); //创建线程池对象,包含2个线程对象
MyRunnable r = new MyRunnable(); //创建Runnable实例对象,需要另外创建一个MyRunnable实现类
Thread t = new Thread(r); //创建线程对象
t.start(); //调用MyRunnable中的run()
service.submit(r);//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);//再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
for(int i=0;i<10;i++) {
//从线程池获取10个线程对象,然后调用MyRunnable中的run()
service.submit(r);
//注意!!!sumbit()方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用的线程又归还到线程池中
}
service.shutdown(); //关闭线程池
}
}
package cn.itcats.thread.Test1;
public class MyRunnable implements Runnable {
@Override
//开始写接口实现类的run()方法
public void run() {
System.out.println("1111111");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("222222222: " +Thread.currentThread().getName());
System.out.println("333333333333333");
}
}
package cn.itcats.thread.Test1;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo1 {
public static void main(String[] args) throws InterruptedException,ExecutionException{
ExecutorService threadPool = Executors.newFixedThreadPool(2); //创建线程池,包含两个线程对象
MyCallable c = new MyCallable(100,200);// 创建MyCallable接口子类对象
MyCallable c2 = new MyCallable(10,20);
Future<Integer> result = threadPool.submit(c); //Future结果对象
Integer sum = result.get(); //Future的get方法所返回的结果类型
System.out.println("sum="+sum);
result = threadPool.submit(c2);
sum = result.get();
System.out.println("sum="+sum);
}
}
package cn.itcats.thread.Test1;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
//成员变量
int x = 5;
int y = 3;
//构造方法
public MyCallable() {
}
public MyCallable(int x,int y) {
this.x=x;
this.y=y;
}
@Override
public Integer call() throws Exception{
return x+y;
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建票对象
Ticket ticket = new Ticket();
//创建3个窗口
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
//同时进行卖票操作
t1.start();
t2.start();
t3.start();
}
}
public class Ticket implements Runnable {
//共100票
int ticket = 100;
@Override
public void run() {
//模拟卖票
while(true){
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
}
同步代码块:在代码块声明上 加上synchronized
synchronized(锁对象){
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能保证线程安全
package cn.itcats.thread.Test1;
public class Ticket implements Runnable{
int ticket = 100;
Object lock = new Object();//定义锁对象
@Override
public void run() {
while(true) {
/*synchronized(锁对象){
* 可能会产生问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能保证线程安全
*/
synchronized(lock) {
//同步代码块,
if(ticket>0) {
try {
Thread.sleep(1);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticket--);
}
}
}
}
}
package cn.itcats.thread.Test1;
public class ThreadDemo {
public static void main(String[] args) {
Ticket1 ticket = new Ticket();
Thread t1 = new Thread(tickets,"窗口1");
Thread t2 = new Thread(tickets,"窗口2");
Thread t3 = new Thread(tickets,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
package cn.itcats.thread.Test1;
public class Tickets implements Runnable {
int tickets = 100;
Object lock = new Object();
public void run() {
while(true) {
method();
}
}
public synchronized void method() {
if(tickets>0) {
try {
Thread.sleep(10);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+tickets--);
}
}
}
package cn.itcats.thread.Test1;
public class ThreadDemo {
public static void main(String[] args) {
Ticket1 tickets = new Ticket1();
Thread t1 = new Thread(tickets,"窗口1");
Thread t2 = new Thread(tickets,"窗口2");
Thread t3 = new Thread(tickets,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
synchronized(A锁){
synchronized(B锁){
}
}
package cn.itcats.thread.Test1;
//定义锁对象类
public class MyLock {
public static final Object lockA = new Object();
public static final Object lockB = new Object();
}
package cn.itcats.thread.Test1;
import java.util.Random;
public class ThreadTask implements Runnable{
int x = new Random().nextInt(1);//随机取0-1的数值
//指定线程要执行的任务代码
@Override
public void run() {
System.out.println("x= "+x);
while(true) {
if(x%2==0) {
synchronized(MyLock.lockA) {
System.out.println("if-LockA");
synchronized(MyLock.lockB) {
System.out.println("if-LockB");
System.out.println("123231");
}
}
}else {
synchronized(MyLock.lockB) {
System.out.println("else-LockB");
synchronized(MyLock.lockA) {
System.out.println("else-LockA");
System.out.println("6666666");
}
}
}
x++;
}
}
}
package cn.itcats.thread.Test1;
//测试类
public class ThreadDemo2 {
public static void main(String[] args) {
ThreadTask task = new ThreadTask();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
package cn.itcats.thread.Test1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket1 implements Runnable{
int ticket1 = 100;
Lock ck = new ReentrantLock();//创建Lock锁对象
@Override
public void run() {
//模拟卖票
while(true) {
ck.lock();//获取锁
if(ticket1>0) {
//模拟选座的操作
try {
Thread.sleep(10); //等待10s。可能会出错的代码
}catch(InterruptedException e) {
//(异常类型 异常对象引用)
e.printStackTrace(); //用于处理异常的代码
}
System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticket1--);
}
ck.unlock();//释放锁
}
}
}
package cn.itcats.thread.Test1;
public class ThreadDemo {
public static void main(String[] args) {
Ticket1 tickets = new Ticket1();
Thread t1 = new Thread(tickets,"窗口1");
Thread t2 = new Thread(tickets,"窗口2");
Thread t3 = new Thread(tickets,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
@[3.5等待唤醒机制]
等待唤醒机制所涉及到的方法:
l wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
l notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
l notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。
仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?
因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。
如上图说示,输入线程向Resource中输入name ,sex , 输出线程从资源中输出,先要完成的任务是:
l 1.当input发现Resource中没有数据时,开始输入,输入完成后,叫output来输出。如果发现有数据,就wait();
l 2.当output发现Resource中没有数据时,就wait() ;当发现有数据时,就输出,然后,叫醒input来输入数据。
package cn.itcats.thread.Test1;
//模拟资源类
public class Resource {
private String name;
private String sex;
private boolean flag = false; //定义标记位
public synchronized void set(String name,String sex) {
//同步{}内的为可能不安全的代码
if(flag) //当标记为false时
try {
wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
this.name = name;//设置成员变量
this.sex = sex;
flag = true;//设置后Resource中有值,将标记改为true
this.notify();
}
public synchronized void out() {
if(!flag)//标记为true
try {
wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("姓名:"+ name +",性别: "+ sex);
flag = false;//将标记改为false
this.notify();//唤醒output
}
}
package cn.itcats.thread.Test1;
//输入线程任务类
public class Input implements Runnable {
private Resource r;
public Input(Resource r) {
this.r = r; //设置成员变量
}
@Override
public void run() {
int count = 0;
while(true) {
if(count == 0) {
r.set("小明", "男生");
}else {
r.set("小红","女生");
}
count = (count+1)%2;//在两个数据之间进行切换
}
}
}
package cn.itcats.thread.Test1;
//输出线程任务类
public class Output implements Runnable {
private Resource r;
public Output(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.out();
}
}
}
package cn.itcats.thread.Test1;
//测试类
public class ResourceDemoTest {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
多个线程想保证线程安全,必须要使用同一个锁对象
l 同步代码块
synchronized (锁对象){
可能产生线程安全问题的代码
}
同步代码块的锁对象可以是任意的对象
l 同步方法
public synchronized void method()
可能产生线程安全问题的代码
}
同步方法中的锁对象是 this
l 静态同步方法
public synchronized void method()
可能产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class
l 多线程有几种实现方案,分别是哪几种?
a, 继承Thread类
b, 实现Runnable接口
c, 通过线程池,实现Callable接口
l 同步有几种方式,分别是什么?
a,同步代码块
b,同步方法
静态同步方法
l 启动一个线程是run()还是start()?它们的区别?
启动一个线程是start()
区别:
start: 启动线程,并调用线程中的run()方法
run : 执行该线程对象要执行的任务
l sleep()和wait()方法的区别
sleep: 不释放锁对象, 释放CPU使用权
在休眠的时间内,不能唤醒
wait(): 释放锁对象, 释放CPU使用权
在等待的时间内,能唤醒
l 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
锁对象可以是任意类型的对象
此文参考于https://www.cnblogs.com/jmsjh/p/7762167.html