java 多线程 独立处理_Java笔记9(多线程)

首先了解一下概念:

进程是系统进行资源分配和调度的一个独立单位,进程包含以下3个特征:

独立性:拥有自己的资源与空间

动态性:动态执行的过程

并发性:可以运行多个进程在一个处理器上,多个进程之间相互不影响

多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程也被称为轻量级的进程。每个进程启动都会启动一个主线程,也可以创建多个辅助的线程。线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程的所有资源。线程是独立运行的,也是抢占式,一个线程可以创建和撤销另一个线程。

线程的创建和启动

Thread类代表线程,所有的线程都是Thread类的子类或者实例。

继承Thread类创建线程类

定义Thread类的子类,并重写该类的run方法,该run方法就代表了线程需要完成的任务。

创建Thread子类的实例。

调用对象的start方法来启动线程。

public class Thread1 extends Thread{

public void run() {

System.out.println(getName() + " hello");

}

public static void main(String[] args) {

new Thread1().start();

}

}

Thread.currentThread() currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象。

getName() 该方法是Thread类的实例方法,该方法返回调用该方法的线程名字。

程序也可以通过使用setName(String name)方法为线程设置名字。

实现Runnable接口创建线程类

定义Runnable接口的实现类,并重写run方法

创建Runnable实现类的实例,并把该对象作为参数传给Thread创建Thread对象(如果启动多个线程,可以同时使用同一个Runnable实例)

调用Thread对象的start()方法来启动线程。

public class Thread2 implements Runnable{

public void run() {

System.out.println(Thread.currentThread().getName() + " hello");

}

public static void main(String[] args) {

Thread2 thread2 = new Thread2();

new Thread(thread2).start();

new Thread(thread2).start();

}

}

* 使用Callable 和 Future 创建线程

* Java 5开始,Java提供了Callable接口,该接口提供了一个call方法可以作为线程执行体。但是call方法比run方法功能更加强大。 * call方法可以有返回值 * call方法可以声明抛出异常

* 创建并启动有返回值的线程步骤如下:

* 创建Callable接口的实现类,并实现call方法,该call方法将作为线程执行体,且该call方法有返回值

* 创建callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值。

* 使用FutureTask对象作为Thread对象的target创建并茄冬新线程

* 调用FutureTask对象的get方法来获得子线程执行结束后的返回值

```

public class Thread3 implements Callable{

public static void main(String[] args) {

Thread3 thread3 = new Thread3();

FutureTask task = new FutureTask(thread3);

new Thread(task).start();

}

@Override

public Integer call() throws Exception {

System.out.println(Thread.currentThread().getName());

return 10;

}

}

```

# 线程的生命周期

线程的生命周期中包括: 新建、就绪、运行、阻塞和死亡。 new Thread/start/…/…/…

线程死亡有以下三种状态:

* run()或call()方法执行完成,线程正常结束

* 线程抛出一个未捕获的Exception 或 Error

* 直接调用线程的stop方法来结束该线程,该方法容易导致死锁,通常不推荐

不要对处于死亡状态的线程调用start方法,程序只能对新建状态的线程调用start方法,对新建的线程两次调用start方法也是错误的,这都会引发IllegalThreadStateExcpetion异常。

# 控制线程

join线程,Thread提供了让一个线程等待另一个线程完成的方法–join方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞。知道被join方法加入的join线程执行完为止。

public class Thread4 extends Thread {

public Thread4(String name) {

super(name);

}

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + " " + i);

}

}

public static void main(String[] args) throws InterruptedException {

// 启动子进程

new Thread4("new thread").start();

for (int i = 0; i < 100; i++) {

if (i == 20) {

Thread4 th = new Thread4("joined thread");

th.start();

th.join();

}

System.out.println(Thread.currentThread().getName() + " " + i);

}

}

}

* 上面程序一共有三个线程,主线程调用了th.join(),则表示主线程要等待th线程执行完之后才开始执行main线程。

* join方法还存在另一种为: join(long millis) 等待join的线程的时间最长为millis毫秒。如果在millis毫秒内被join的线程还没有执行结束,则不再等待。

# 线程同步

为了解决多个线程针对同一代码的调用而引起的操作异常,可以使用同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块,同步代码块的语法格式如下:

sychronized(obj) {

}

任何时刻只有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。

虽然可以是使用任何对象作为同步监视器,但是通常推荐使用可能被并发访问的共享资源充当同步监视器。

与同步代码块对应的是,Java多线程安全还支持提供同步方法,同步方法就是使用sychronized关键字来修饰某个方法,则该方法称为同步方法。对于同步方法而言无需显示指定同步监视器,同步方法的同步监视器就是this,也就是该对象本身。使用方法如下:

public sychronized void draw() {

}

sychronized 关键字可以修饰方法,可以修饰代码块,但不能修饰构造器和属性。

JDK提供的StringBuilder 和 StringBuffer两个类,前者就是线程不安全的,后者是线程安全类。前者的性能和效率高些,后者性能和效率低些。

* 在当前线程调用wait方法后,该线程会释放同步监视器

* 在当前线程调用Thread.sleep() 和 Thread.yield()方法来暂停当前线程的执行是不会释放同步监视器。

* 线程执行同步代码块时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。

**使用同步锁(Lock)来同步线程:**

Lock 提供了比sychronized方法和synchronized代码块更为广泛的锁定操作,Lock实现允许更加灵活的结构。它提供了ReadWriteLock(读写锁)和Lock两个根接口,并为Lock提供了ReentrantLock(可重入锁 )实现类。为ReadWriteLock提供了ReentrantReadWriteLock实现类。实现代码如下:

class X {

private final ReentrantLock lock = new ReentrantLock();

public void m() {

// 加锁

lock.lock();

try {

....

}

// 使用finally 块来保证释放锁

finally {

lock.unlock();

}

}

}

当两个线程相互等待对方释放同步监视器就会发生死锁,Java虚拟机没有监测,也没有采取措施处理死锁情况。所以多线程编程时应该采取措施避免死锁的出现。

# 线程通信

程序是无法控制线程的调度的,如果要实现两个线程同时交替执行,不能一个线程连续执行两次,这种线程同步方式,可以借助于Object类提供的wait() notify() 和 notifyAll() 3个方法,这三个方法是属于Object类自带方法,但是这三个方法必须由同步监视器对象来调用。

* 对于使用sychronized修饰的同步方法,this就是同步监视器,所以可以在同步方法中直接调用这3个方法

* 对于使用sychronized修饰的同步代码块,同步监视器是synchronized后括号中的对象,由这个对象来调用这三个同步方法。

关于这三个方法的解释如下:

* wait(): 导致线程等待,有三种形式:一种是无时间参数的wait,一种是带毫秒的,另一种是带微妙的。

* notify(): 唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则会选择其中的一个,这个是随机选择的。

* nofifyAll():唤醒。。。。。。。。。。所有线程。

如果使用Lock来同步代码,这样的话没有同步监视器,这样java提供了一个Condition,Lock替代了同步方法或同步代码块,Condition替代了同步监视器的功能。

Condition实例被绑定在一个Lock对象上,要获得特定Lock实例的Condition实例,调用Lock对象的newCondition()方法即可。提供了如下三个方法:

* await() – > wait

* signal() – > notify()

* signalAll() – > notifyAll()

# 线程池

系统启动一个新线程的成本很高,在这种情形下,需要使用线程池来提高性能。

Java 5新增了一个Executors工厂类来产生线程池,该工厂类包含如下的几个方法:

* newCachedThreadPool(),创建一个具有缓存功能的线程池,系统根据需要创建线程的个数,这些线程将会被缓存在线程池中

* newFixedThreadPool(int nThread), 创建一个可重用的,具有固定线程数的线程池。

* newSingleThreadPool()创建一个只具有单线程的线程池,它相当于调用newFixedThreadPool(1),传入的参数为1

* newScheduledThreadPool(int corePoolSize)创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。

前面三个方法是返回一个ExecutorService的对象,该对象提供了submit的方法来提交一个线程。

class MyThread implements Runnable {

public void run() {

....

}

}

public class ThreadPoolTest {

public stati void main(String[] args) {

ExecutorService pool = Executors.newFixedThreadPool(6);

pool.submit(new MyThread());

pool.submit(new MyThread());

pool.shutdown();

}

}

---

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值