package com.javase.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Ticket {
private int number = 30;
private Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if(number > 0) {
Thread.sleep(300);
System.out.println(Thread.currentThread().getName() + "卖出第" + (number--) + "张票,还剩" + (number)+"张票。");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 3个售票员,卖30张票
* @author Bo
*
*/
public class ThreadDemo0 {
public static void main(String[] args) {
final Ticket ticket = new Ticket();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"AA").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"BB").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"CC").start();
}
}
package com.javase.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class Thread1 implements Runnable{
@Override
public void run() {
System.out.println("runable come in...");
}
}
class Thread2 implements Callable<Integer> {
private int number = 30;
@Override
public Integer call() throws Exception {
if(number > 0) {
System.out.println(Thread.currentThread().getName() + "在卖第" + number-- + "还剩" + number+"张票。");
}
return 200;
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Thread2());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
- 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1, 实现交替,来10轮,变量初始值为零。
方法①:使用的同步锁synchronized
package com.javase.thread;
class ShareData {
int number = 0;
public synchronized void increment() {
while (number != 0) {//这里要用while,因为使用if,会造成几个线程同时运行。
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
++number;
System.out.println(Thread.currentThread().getName() +"\t"+ number);
notifyAll();
}
public synchronized void decrement() {
while (number == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
--number;
System.out.println(Thread.currentThread().getName() +"\t"+ number);
notifyAll();
}
}
/**
* 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1, 实现交替,来10轮,变量初始值为零。
*
* @author Bo
*
*/
public class ThreadDemo2 {
public static void main(String[] args) {
final ShareData shareData = new ShareData();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.increment();
}
}
},"AA").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.decrement();
}
}
},"BB").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.increment();
}
}
},"CC").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.decrement();
}
}
},"DD").start();
}
}
方法②:使用lock
package com.javase.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareData_2 {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() {
lock.lock();
try {
while (number != 0) {
condition.await();
}
++number;
System.out.println(Thread.currentThread().getName() +"\t"+ number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
while(number == 0) {
condition.await();
}
--number;
System.out.println(Thread.currentThread().getName() +"\t"+ number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1, 实现交替,来10轮,变量初始值为零。 方法2:使用lock
*
* @author Bo
*
*/
public class ThreadDemo2_2 {
public static void main(String[] args) {
final ShareData_2 data_2 = new ShareData_2();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
data_2.increment();
}
}
},"AA").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
data_2.decrement();
}
}
},"BB").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
data_2.increment();
}
}
},"CC").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
data_2.decrement();
}
}
},"DD").start();
}
}
package com.javase.thread;
class TestSynchronized {
public static synchronized void getIOS() throws InterruptedException {
Thread.sleep(300);
System.out.println("IOS");
}
public synchronized void getAndroid() {
System.out.println("Android");
}
public void getHello() {
System.out.println("Hello");
}
}
/**
* 测试8锁ytt5t66yhr
*1 标准普通调用,先打印苹果还是android ==>IOS
*2 新添加Thread.sleep(4000),先打印苹果还是android ==>IOS
*3 新增加Hello方法,先打印苹果还是hello ==>hello
*4 有两部手机,先打印苹果还是android ==>android
*5 两个静态同步方法,同一部手机,先打印苹果还是android ==>IOS
*6 两个静态同步方法,有两部手机,先打印苹果还是android ==>IOS
*7 一个静态同步方法,一个普通同步方法,同一部手机,先打印苹果还是android ==>android
*8 一个静态同步方法,一个普通同步方法,有两部手机,先打印苹果还是android ==>android
* @author Bo
*
*/
public class ThreadDemo3 {
public static void main(String[] args) {
final TestSynchronized tel = new TestSynchronized();
final TestSynchronized tel2 = new TestSynchronized();
new Thread(new Runnable() {
public void run() {
try {
tel.getIOS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
tel2.getAndroid();
}
}).start();
// new Thread(new Runnable() {
// public void run() {
// tel.getHello();
// }
// }).start();
}
}
线程锁笔记
- 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
- 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
- 加个普通方法后发现和同步锁无关
- 换成两个对象后,不是同一把锁了,情况立刻变化。
- 都换成静态同步方法后,情况又变化
- 所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
- 所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
|
- 备注:多线程之间按顺序调用,实现A->B->C 三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次 接着 AA打印5次,BB打印10次,CC打印15次
共计来20轮
package com.javase.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyResource {
private int number = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void loopA(int loop_id) {
lock.lock();
try {
while(number != 1) {
condition1.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() +"\t"+ i + " : " + loop_id);
}
//标记+唤醒
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopB(int loop_id) {
lock.lock();
try {
while(number != 2) {
condition2.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() +"\t"+ i + " : " + loop_id);
}
//标记+唤醒
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopC(int loop_id) {
lock.lock();
try {
while(number != 3) {
condition3.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() +"\t"+ i + " : " + loop_id);
}
System.out.println();
//标记+唤醒
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
*
* @author Bo 备注:多线程之间按顺序调用,实现A->B->C 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次 接着 AA打印5次,BB打印10次,CC打印15次
*
* 共计来20轮
*/
public class ThreadDemo4 {
public static void main(String[] args) {
final MyResource myResource = new MyResource();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
myResource.loopA(i+1);
}
}
},"AA").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
myResource.loopB(i+1);
}
}
},"BB").start();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
myResource.loopC(i+1);
}
}
},"CC").start();
}
}
package com.javase.thread;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Resource_RWLock {
private int number = 1;
private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
private Object obj;
public void set(Object obj) {
rwlock.writeLock().lock();
try {
this.obj = obj;
System.out.println(Thread.currentThread().getName() + " : " + obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwlock.writeLock().unlock();
}
}
public void get() {
rwlock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " : " + obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwlock.writeLock().unlock();
}
}
}
/**
*读写锁
*一个线程写,100个线程读取
* @author Bo
*
*/
public class ThreadDemo5 {
public static void main(String[] args) {
final Resource_RWLock rwlock = new Resource_RWLock();
new Thread(new Runnable() {
public void run() {
rwlock.set("上车上车");
}
},"AA").start();
for (int i = 1; i <= 100; i++) {
new Thread(new Runnable() {
public void run() {
rwlock.get();
}
},"BB"+i).start();
}
}
}
private static void threadPoolTest() {
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//指定几个线程
// ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();//单个
ExecutorService threadPool = Executors.newCachedThreadPool();//创建一个可根据需要创建新线程的线程池
Future<Integer> result = null;
try {
for (int i = 0; i < 200; i++) {
result = threadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.print(Thread.currentThread().getName() + "\t");
return new Random().nextInt(20);
}
});
System.out.println(result.get());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
public static void main(String[] args) {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);//可以指定线程之间相隔时间
//ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<Integer> result = null;
try {
for (int i = 0; i < 20; i++) {
result = threadPool.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.print(Thread.currentThread().getName() + "\t");
return (int) (Math.random() * 20);
}
}, 2, TimeUnit.SECONDS);
System.out.println(result.get());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
一、进程
进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。
注意,进程一般有三个状态:就绪状态、执行状态和等待状态【或称阻塞状态】;进程只能由父进程建立,系统中所有的进程形成一种进程树的层次体系;挂起命令可由进程自己和其他进程发出,但是解除挂起命令只能由其他进程发出。
进程控制块(PCB):PCB不但可以记录进程的属性信息,以便操作系统对进程进行控制和管理,而且PCB标志着进程的存在,操作系统根据系统中是否有该进程的进程控制块PCB而知道该进程存在与否。系统建立进程的同时就建立该进程的PCB,在撤销一个进程时,也就撤销其PCB,故进程的PCB对进程来说是它存在的具体的物理标志和体现。一般PCB包括以下三类信息:进程标识信息;处理器状态信息;进程控制信息。
由程序段、相关的数据段和PCB三部分构成了进程实体(又称进程印像),一般,我们把进程实体就简称为进程。
进程的特征:
1.动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。
2.并发性:任何进程都可以同其他进程一起并发执行。
3.独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
4.异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
二、线程
线程:线程是进程中的一个实体,作为系统调度和分派的基本单位。Linux下的线程看作轻量级进程。
线程的性质:
1.线程是进程内的一个相对独立的可执行的单元。若把进程称为任务的话,那么线程则是应用中的一个子任务的执行。
2.由于线程是被调度的基本单元,而进程不是调度单元。所以,每个进程在创建时,至少需要同时为该进程创建一个线程。即进程中至少要有一个或一个以上的线程,否则该进程无法被调度执行。
3.进程是被分给并拥有资源的基本单元。同一进程内的多个线程共享该进程的资源,但线程并不拥有资源,只是使用他们。
4.线程是操作系统中基本调度单元,因此线程中应包含有调度所需要的必要信息,且在生命周期中有状态的变化。
5.由于共享资源【包括数据和文件】,所以线程间需要通信和同步机制,且需要时线程可以创建其他线程,但线程间不存在父子关系。
多线程使用的情形:前台和后台工作情况;异步处理工作情况;需要加快执行速度情况;组织复杂工作的情况;同时有多个用户服务请求的情况等。
线程机制的优点:
多线程运行在同一个进程的相同的地址空间内,和采用多进程相比有以下优点:
1.创建和撤销线程的开销较之进程要少。创建线程时只需要建立线程控制表相应的表目,或有关队列,而创建进程时,要创建PCB表和初始化,进入有关进程队列,建立它的地址空间和所需资源等。
2.CPU在线程之间开关时的开销远比进程要少得多。因开关线程都在同一地址空间内,只需要修改线程控制表或队列,不涉及地址空间和其他工作。
3.线程机制也增加了通讯的有效性。进程间的通讯往往要求内核的参与,以提供通讯机制和保护机制,而线程间的通讯是在同一进程的地址空间内,共享主存和文件,无需内核参与。
三、进程和线程的区别
(1)调度:
在传统的操作系统中,CPU调度和分派的基本单位是进程。而在引入线程的操作系统中,则把线程作为CPU调度和分派的基本单位,进程则作为资源拥有的基本单位,从而使传统进程的两个属性分开,线程编程轻装运行,这样可以显著地提高系统的并发性。同一进程中线程的切换不会引起进程切换,从而避免了昂贵的系统调用,但是在由一个进程中的线程切换到另一进程中的线程,依然会引起进程切换。
(2)并发性:
在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间也可以并发执行,因而使操作系统具有更好的并发性,从而更有效地提高系统资源和系统的吞吐量。例如,在一个为引入线程的单CPU操作系统中,若仅设置一个文件服务进程,当它由于某种原因被封锁时,便没有其他的文件服务进程来提供服务。在引入线程的操作系统中,可以在一个文件服务进程设置多个服务线程。当第一个线程等待时,文件服务进程中的第二个线程可以继续运行;当第二个线程封锁时,第三个线程可以继续执行,从而显著地提高了文件服务的质量以及系统的吞吐量。
(3)拥有资源:
不论是引入了线程的操作系统,还是传统的操作系统,进程都是拥有系统资源的一个独立单位,他可以拥有自己的资源。一般地说,线程自己不能拥有资源(也有一点必不可少的资源),但它可以访问其隶属进程的资源,亦即一个进程的代码段、数据段以及系统资源(如已打开的文件、I/O设备等),可供同一个进程的其他所有线程共享。
(4)独立性:
在同一进程中的不同线程之间的独立性要比不同进程之间的独立性低得多。这是因为
为防止进程之间彼此干扰和破坏,每个进程都拥有一个独立的地址空间和其它资源,除了共享全局变量外,不允许其它进程的访问。但是同一进程中的不同线程往往是为了提高并发性以及进行相互之间的合作而创建的,它们共享进程的内存地址空间和资源,如每个线程都可以访问它们所属进程地址空间中的所有地址,如一个线程的堆栈可以被其它线程读、写,甚至完全清除。
(5)系统开销:
由于在创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O设备等。因此,操作系统为此所付出的开销将显著地大于在创建或撤消线程时的开销。类似的,在进程切换时,涉及到整个当前进程CPU环境的保存环境的设置以及新被调度运行的CPU环境的设置,而线程切换只需保存和设置少量的寄存器的内容,并不涉及存储器管理方面的操作,可见,进程切换的开销也远大于线程切换的开销。此外,由于同一进程中的多个线程具有相同的地址空间,致使他们之间的同步和通信的实现也变得比较容易。在有的系统中,现成的切换、同步、和通信都无需操作系统内核的干预。
(6)支持多处理机系统:
在多处理机系统中,对于传统的进程,即单线程进程,不管有多少处理机,该进程只能运行在一个处理机上。但对于多线程进程,就可以将一个进程中的多个线程分配到多个处理机上,使它们并行执行,这无疑将加速进程的完成。因此,现代处理机OS都无一例外地引入了多线程。
|