目录
使用synchronized(同步监视器)关键字同步方法或代码块
1. 程序、进程、线程
程序:为了完成某种功能,使用计算机编程语言完成任务的一组集合。(指一段静态代码)
进程:指正在执行任务的一组程序,即在计算机操作系统中进行分配资源的基本单位
线程:为了降低时空开销,在操作系统中引入线程,线程是系统调度的基本单位。
为什么引入线程的目的?
进程在OS中的进行创建、撤销、切换等操作的时候时空开销较大,为了减少程序在并发执行时所付出的时空开销,使0S具有更好的并发性,提高系统性能和CPU的利用率。
线程和进程之间的关系
- 一个进程拥有多个线程,一个线程属于一个进程,线程不能脱离进程单独运行;
- 进程是计算机OS中进行分配资源的最小单位,而线程是系统调度的基本单位;
- 在进程中的所用线程可以共享该进程的所拥有的内存资源;
- 每一个进程都至少包含一个主线程。(java中使用一个主线程进行启动程序)
2. 创建线程
1. 继承Thread类创建线程
Thread 类 用于创建线程,操作线程的类 创建主线程:继承Thread类
public class Demo extends Thread{
/*
java 中要在线程中执行的任务,写在run()
*/
@Override
public void run(){
for (int i = 0; i < 500; i++) {
System.out.println("ThreadDemo1: " + i);
}
}
}
public class Test {
/*
main 启动java程序的主线程
*/
public static void main(String[] args) {
//在主线程中创建线程,并启动线程
Demo td = new Demo();
//td.run(); 千万切记调用run() 方法启动线程这只是一个普通方法调用,不是启动线程
td.start();//启动线程start0(); private native void start0();
//告诉操作系统开启线程,开始调度
for (int i = 0; i < 500; i++) {
System.out.println("main: " + i);
}
//主线程和Demo1 中的线程交替运行,主要看CPU进行调度
}
}
2. 实现Runnable接口创建线程
public class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("MyThread: " + i);
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myth = new MyThread();
Thread th = new Thread(myth);
th.start();
}
}
两种创建线程的比较:
- 避免继承的局限,一个类可以继承多个接口。
- 适合于资源的共享。
- 增加程序的健壮性。
3. 线程中常用方法
- void start(); 启动线程
- final void setName(String name) 设置线程的名称
- final String getName() 返回线程的名称
- final void setPriority(int newPriority) 设置线程的优先级
- final int getPriority() 返回线程的优先级
- final void join() throws InterruptedException 等待线程终止
- static Thread currentThread() 返回对当前正在执行的线程对象的引用
- static void sleep(long millis) throws InterruptedException 让当前正在执行的线程休眠(暂停执行), 休眠时间由 millis(毫秒)指定
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getPriority());
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getPriority());
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myth = new MyThread();
Thread th1 = new Thread(myth,"线程1:");//创建了一个线程,并为其分配了一个任务
// th1.setName("线程1");
th1.start();
Thread th2 = new Thread(myth,"线程2:");
th2.start();
Thread.currentThread().setPriority(10);
//java中的线程支持设置优先级的,默认优先级是5,最低是1,最高是10
th1.setPriority(5);
th1.setPriority(1);
for (int i = 0; i < 10; i++) {
System.out.println("main: " + i);
}
System.out.println("mian:" + Thread.currentThread().getPriority());//
}
}
3. 线程状态
4. 线程的分类
java中的线程分类:用户线程和守护线程。
守护线程:任何一个守护线程都是整个JVM中所有非守护线程的保姆: 只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作; 只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
注:设置线程为守护线程,必须在线程启动前设置
public class ThreadDemo1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程");
}
}
}
public class ThreadDemo2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("用户" + i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
ThreadDemo th = new ThreadDemo();
ThreadDemo2 th2 = new ThreadDemo2();
Thread thread1 = new Thread(th,"ThreadDemo1");
Thread thread2 = new Thread(th,"ThreadDemo2");
thread1.setDaemon(true);设置线程为守护线程,必须在线程启动前设置
thread1.start();
thread2.start();
}
}
5. 多线程的概念
指程序中包含多个执行单元,即在一个程序中可以同时运行多 个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行 执行的线程来完成各自的任务。
5.1 线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制, 即各线程间要有先来后到;
并发:两个或多个事件在同一时间间隔内发生
并行:两个或多个事件在同一时刻发生
使用synchronized(同步监视器)关键字同步方法或代码块
synchronized (同步对象)
同步对象可以是任意类的对象,但是只能唯一的,只有一份的,这种情况不能用this,因为我们创建两个线程对象,哪个线程访问,this就表示的是哪个线程,对象中有一个区域叫对象头,对象头中有一个锁状态的记录
/*
创建一个出票的线程
*/
public class TicketThreadback extends Thread{
//static 修饰的变量是类变量,在内存中只有一份
static int num = 10;// 设定有10张票
static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){//进入同步代码块 加锁 也就将对象头锁状态改为 加锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+"买到票:"+num);
num--;
}else{
break;
}
//同步代码块执行完成后,会自动释放锁 也就将对象头锁状态改为 无锁
}
}
}
}
synchronized 修饰非静态方法,锁标志的对象默认是this
synchronized 修饰静态方法,锁标志对象是类的Class对象,只要是同一个类的对象,那么他们对应的Class对象时相同的
public class TicketDemo2 extends Thread{
static Object obj = new Object();
static int num = 10;
@Override
public void run() {
while (true){
tickets();
if(num <= 0){
break;
}
}
}
public synchronized void tickets(){
if(num > 0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "剩余票数:" + num);
num--;
}
}
}
public class TestDemo2 {
public static void main(String[] args) {
TicketDemo2 th1 = new TicketDemo2();
th1.setName("线上预订");
th1.start();
TicketDemo2 th2 = new TicketDemo2();
th2.setName("线下买票");
th2.start();
}
}
实现Runnable 接口
只创建了一个对象,所以两个线程拿到的this是同一个
public class TicketThread implements Runnable{
int num = 10;
@Override
public void run() {
while (true){
synchronized (this){
if(num > 0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "剩余票数:" + num);
num--;
}else{
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
Thread th1 = new Thread(ticketThread,"线上预订");
Thread th2 = new Thread(ticketThread,"线下买票");
th1.start();
th2.start();
}
}
5.2 Lock(锁)
synchronized 和 ReentrantLock类区别
synchronized
- 是关键字
- 修饰方法和代码块
- 隐式的加锁和释放锁
- 底层通过java虚拟机来进行控制实现
Lock接口 ReentrantLock类
- 是类
- 只能修饰代码块
- 显式的加锁和释放锁
- 底层通过java代码来进行控制实现
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketDemo extends Thread{
static int num = 10;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try {
lock.lock();
if (num > 0) {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + "剩余票数:" + num);
num--;
} else {
break;
}
} catch (Exception e) {
lock.unlock();
}finally {
lock.unlock();//释放锁
}
}
}
}
public class TestDemo {
public static void main(String[] args) {
TicketDemo th1 = new TicketDemo();
th1.setName("线上预订");
TicketDemo th2 = new TicketDemo();
th2.setName("线下买票");
th1.start();
th2.start();
}
}
5.3 线程死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃 自己需要的同步 资源,就形成了线程的死锁。
public class DieLockThread extends Thread{
static Object objA = new Object();
static Object objB = new Object();
boolean flag = true;
public DieLockThread(boolean flag){
this.flag = flag;
}
@Override
public void run(){
if(flag){
synchronized (objA){
System.out.println("if objA");
synchronized (objB){
System.out.println("if objB");
}
}
}else{
synchronized (objB){
System.out.println("else objB");
synchronized (objA){
System.out.println("else objA");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
DieLockThread t1 = new DieLockThread(true);
DieLockThread t2 = new DieLockThread(false);
t1.start();
t2.start();
}
}
5.4 线程通信
定义;指多个线程通过相互牵制,相互调度,即线程间的相互作用。
- wait(); 线程阻塞,并同时释放同步锁
- notify(); 唤醒被wait() 的一个线程,如果有多个线程被wait(); 会唤醒一个优先级高的线程。
- notifyAll(); 唤醒所有被wait()的线程
使用前提:必须在同步代码块中使用
public class SumThread extends Thread{
static int num = 0;
static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){
obj.notify();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Test {
public static void main(String[] args) {
SumThread t1 = new SumThread();
SumThread t2 = new SumThread();
t1.start();
t2.start();
}
}
经典例题:生产者和消费者问题
Counter 柜台类
public class Counter {
int num = 1;
/*
生产者生产商品
由于只创建了一个Counter对象,synchronized修饰方法,同步锁对象时this,jia和jain 两个方法公用同一把锁
*/
public synchronized void jia(){
Random random = new Random();
if(num==0){
num+=random.nextInt(10);
System.out.println("生产者生产商品:"+num);
this.notify();//生产者生产后,唤醒消费者
}else{
try {
this.wait();//柜台有商品的,直接等待,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
消费者取走商品
*/
public synchronized void jian(){
if(num>0){
num--;
System.out.println("消费者拿走商品:"+num);
this.notify();//消费者拿走商品,唤醒生产者
}else{
try {
this.wait();//没有商品,消费者等待,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
CustomerTread 消费者线程
public class CustomerTread extends Thread{
Counter counter;
public CustomerTread(Counter counter){
this.counter = counter;
}
public void run(){
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
counter.jian();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ProducerTread 消费者线程
public class ProducerTread extends Thread{
Counter counter;
public ProducerTread(Counter counter){
this.counter = counter;
}
public void run(){
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
counter.jia();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
Counter counter = new Counter();
CustomerTread c = new CustomerTread(counter);
ProducerTread p = new ProducerTread(counter);
c.start();
p.start();
}
}
6. 新增创建线程方法
实现Callable接口与使用Runnable相比,Callable功能更强大些
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,获取返回结果
接收任务 FutureTask futureTask = new FutureTask(任务);
创建线程 Thread t = new Thread(futureTask); t.start();
Integer val = futureTask.get();获得线程call方法的返回值
import java.util.concurrent.Callable;
public class SumThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 0; i < 5; i++) {
num+=i;
}
return num;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) {
SumThread sumThread = new SumThread();
FutureTask<Integer> task = new FutureTask(sumThread);
Thread thread = new Thread(task);
try {
Integer sum = task.get();
System.out.println("求和:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}