进程(process)是程序的一次执行过程,或是正在运行的有一个程序,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期。
线程(thread),进程可进一步细化线程是一个程序内部的一个执行路径
- 若一个进程用以时间并行执行多个线程,就是支持多线程
- 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序技术器(PC),线程切换开销小
- 一个进程中的多个线程共享相同的内存单元/内存地址空间->他们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就睡带来安全隐患。
- 并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事
- 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀。多个人做同一件事。
- 提高应用程序的响应。对图形化页面更有意义,可增强用户体验
- 提高计算机系统CPU的利用率
- 改善程序结果,将既长又复杂的的进程分为多线程,独立运行,利于理解和改善
- 创建一个继承与Thread类的子类
- 重新Thread了的run()
- 创建Thread类的子类的独享
- 通过此对象调用start():(1)启动当前线程(2)调用当前线程的run()
package com.haust.java;
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread("线程1:");
// h1.setName("线程一:");
h1.start();
//给主线程命名:
Thread.currentThread().setName("主线程:");
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":" +i);
}
if(i==20){
try {
h1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class HelloThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":" +i);
}
if(i%20==0){
yield();
}
}
}
public HelloThread(String name){
super(name);
}
}
线程的调度
- 时间片
- 抢占式:高优先级的线程抢占CPU
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
涉及的方法
- getPriority():返回线程优先值
- setPriority(int newPriority):改变线程的优先级
说明:
- 线程创建时继承父线程的优先级
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
卖票案例(存在线程安全问题)
package com.haust.java;
class Window extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(getName() + "卖票,票号为:"+ticket);
ticket--;
}
else{
break;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
方式二:实现Runnable接口
package com.haust.java;
public class ThreadTest1 {
public static void main(String[] args) {
// 3、创建实现类的对象
MThread mThread = new MThread();
// 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread thread = new Thread(mThread);
// 5、通过Thread类的对象调用start()①启动线程②调用当前线程内的run()方法--->调用了Runnable类型的target的run()
thread.setName("线程1:");
thread.start();
// 再启动一个线程,遍历100以内的偶数
Thread thread1 = new Thread(mThread);
thread1.setName("线程2:");
thread1.start();
}
}
//1、创建实现了Runnable接口的类
class MThread implements Runnable{
//2、实现类去实现Runnable中的抽象方法:run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
}
}
}
比较创建线程的两种方式
开发中优先选择实现Runnable方式
原因:
- 实现的方式没有类的单继承性的局限性
- 实现的方式更适合来处理多个线程共享数据的情况
相同点:都需要重写run(),将线程要执行的代码声明在run()方法中
线程的生命周期解决线程安全问题;
使用同步代码块
package com.haust.java;
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread thread1 = new Thread(w);
thread1.setName("窗口1");
Thread thread2= new Thread(w);
thread2.setName("窗口2");
thread1.start();
thread2.start();
}
}
class Window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "票号:" + ticket);
ticket--;
}
else
{
break;
}
}
}
}
}
使用同步方法
线程的死锁问题 死锁- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
- 专门的算法、原则
- 进来减少同步资源的定义
- 进来避免嵌套同步
package com.haust.java1;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
//1、实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while(true){
try {
//2、调用Lock方法
lock.lock();
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票:"+ticket);
ticket--;
}
else {
break;
}
}finally {
//3、调用解锁方法:unlock()
lock.unlock();
}
}
}
}
存钱案例
package com.haust.exer;
class Account{
private double balance;
public Account(double balance) {
this.balance = balance;
}
//存储
public synchronized void deposit(double amt){
if (amt > 0){
balance += amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"存钱成功,账户余额为:" + balance);
}
}
}
class Customer extends Thread{
private Account account;
public Customer(Account account){
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
account.deposit(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(0);
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
线程通信的例子
package com.haust.java2;
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
synchronized (this){
notify();
if(number <= 100){
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
wait();//使得调用如下wait()方法的进程进入阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else
{
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("A");
t2.setName("B");
t1.start();
t2.start();
}
}
面试题:sleep()方法和wait()方法的异同
相同点:
- 一旦执行方法,都可以使得当前的线程进入阻塞状态
不同点:
- 两个方法声明的位置不同
- Thread类中声明sleep(),Object类中声明wait()
- 调用的要求不同:
- sleep()可以用在任何需要的场景下调用,wait()必须使用在同步代码块或同步方法中
- 关于是否释放同步监视器
- 如果两个方法都是用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁
package com.haust.java2;
class Clerk{
private int productCount = 0;
//生产产品
public synchronized void produceProduct() {
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName()+"开始生产第"+productCount+"个产品");
notify();
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productCount > 0){
System.out.println(Thread.currentThread().getName()+"开始销售第"+productCount+"个产品");
productCount--;
notify();
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始生产产品……");
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始购买产品……");
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk c = new Clerk();
Producer p1 = new Producer(c);
p1.setName("生产者1");
Consumer c1 = new Consumer(c);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
JDK5.0新增线程创建方式
新增方式一:实现Callable接口
与使用Runnable相比,Callable功能更强大一下
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持范型的返回值
- 需要借助FutureTask类,比如获取返回结果
Future接口
- 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等
- FutureTask是Future接口的唯一实现类
- FutureTask同时实现了Runnable,Future接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
package com.haust.java2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1、创建一个实现Callable的实现类
class NumThread implements Callable{
// 2、实现call()方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
// 3、创建callable接口实现类的对象
NumThread numThread = new NumThread();
// 4、将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
Thread thread = new Thread(futureTask);
thread.start();
try {
//get()返回值即为FutureTask构造参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
创建多线程的方式四
package com.haust.java2;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new NumThread1());//适合适用于Runnable
service.execute(new NumThread2());//适合适用于Runnable
// service.submit(Callable callable);//适合用于Callable
}
}
class NumThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if (i %2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if (i %2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
使用线程池的好处
-
提升响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
/ service.submit(Callable callable);//适合用于Callable
}
}
class NumThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if (i %2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);} }
}
}
class NumThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if (i %2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);} }
}
}
## 使用线程池的好处 - 提升响应速度(减少了创建新线程的时间) - 降低资源消耗(重复利用线程池中线程,不需要每次都创建) - 便于线程管理



