1. 创建线程
Java的线程继承自Thread的类,处理一个名为run的方法。
而对于那些不能继承 Thread 的类,可以采取实现 Runnable 接口的方式进行。
2. 执行权转交
对于暂时交出执行权,Java 提供了 Thread.yield() 方法.
结果:
Java 也提供了 Thread.sleep(); 方法,但由于 Thread.sleep 可能被 interrupt( ) 方法中断,因此必须包含在 try{} 代码块中。
3. 优先级
Java 使用 setPriority( ) 方法调整优先级。
4. 背景线程
Java 使用 setDaemon() 方法。在 Java 中一般将背景线程称之为"守护线程(daemon thread)"。需要注意的是即便背景线程未结束,进程依然会终止。必须在线程启动前设置。
5. 线程等待
Java 中都使用 join/Join() 方法阻止调用线程,直到某个线程结束。不过 Java 里面情况还是要复杂一些。当某个线程处于 join 等待时,它可能会被 interrupt( ) 方法中断,因此也得放在 try{} 代码块中。
结果:
6. 资源锁定
Java 提供了 synchronized 关键字用来解决多线程资源共享锁定的问题,synchronized 可用于方法或者某个代码段。
当我们取消 synchronized 关键字时,我们会发现 t1 和 t2 交替执行。添加 synchronized 关键字以后,t2 会在 t1 执行完成后执行,因此对于方法的锁定是成功的。
结果:
另外,需要注意的是 synchronized 是全局锁定,也就是说对于静态方法而言,它们会锁定全部有此标记的方法(尽管它们不是同一个方法);而对于对象实例,它们会锁定同一对象全部有此标记的方法(尽管它们不是同一个方法)。
7. 原子操作
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行倒结束,中间不会有任何线程切换操作。
通常所说的Java原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。不过如果你在long或double前面加了volatile,那么它就肯定是原子操作了。
看下面的例子,虽然 return i是一个原子操作,但是从获得i的值到方法返回之间有可能被其他线程修改,因此不要主观的认为下面的操作是原子操作。我们还是有必要为其添加同步关键字synchronized。另外 ++i 和 --i 都不是原子操作,它们都涉及读和写两个步骤。
《Thinking in Java》中关于最安全的做法提出了如下的方针,不过我不完全赞同。
8. 线程协同
Java用对象锁来控制多线程安全,而所谓多线程协同无非是利用某种锁机制让线程暂时停顿下来,直到某个“信号”发生。为此,Java 将用来进行多线程协同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。
调用 wait() / notify() / notifyAll() 之前必须先获取其对象锁,否则会抛出"IllegalMonitorStateException - current thread not owner" 异常。
输出
1
2
3
4
5
Notify Thread...
6
7
8
9
在上面这个例子中,当 i 等于 5 时,我们调用了对象锁 o 的 wait 方法阻塞线程。在 main 方法中我们循环检查对象状态,并适时发出信号结束等待。这个例子虽然很简单,但是由此可以处理多个线程之间的协同关系。当然,我们还可以给 wait 方法加一个参数,让其等待特定的时间,而不是上面例子中的无限制等待。
需要注意的是 wait() 会释放对象锁,sleep() 则不会。接着看下面的例子。
输出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5
9. 停止/中断线程
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
Java 不推荐使用 stop() / about() / suspend() / resume() 之类的方法来停止线程。原因包括:
当线程因某种原因处于阻塞等待状态时,我们就无法使用旗标终止它,那么改用interrupt()好了。
Java的线程继承自Thread的类,处理一个名为run的方法。
class MyThread extends Thread {
private int i = 0;
public void run() {
while (true) {
System.out.println(++i);
if (i > 100) break;
}
}
}
public class program {
public static void main(String[] args){
new MyThread().start();
}
}
private int i = 0;
public void run() {
while (true) {
System.out.println(++i);
if (i > 100) break;
}
}
}
public class program {
public static void main(String[] args){
new MyThread().start();
}
}
而对于那些不能继承 Thread 的类,可以采取实现 Runnable 接口的方式进行。
2. 执行权转交
对于暂时交出执行权,Java 提供了 Thread.yield() 方法.
class MyThread extends Thread {
public void run() {
int i = 0;
while (true) {
System.out.println( getName()+"-----------"+(++i));
if (i > 100) break;
yield();
}
}
}
public class ThreadTest {
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
public void run() {
int i = 0;
while (true) {
System.out.println( getName()+"-----------"+(++i));
if (i > 100) break;
yield();
}
}
}
public class ThreadTest {
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
结果:
t1-----------1
t2-----------1
t1-----------2
t2-----------2
t1-----------3
t2-----------3
t1-----------4
t2-----------4
t1-----------5
t2-----------5
.....
t2-----------1
t1-----------2
t2-----------2
t1-----------3
t2-----------3
t1-----------4
t2-----------4
t1-----------5
t2-----------5
.....
Java 也提供了 Thread.sleep(); 方法,但由于 Thread.sleep 可能被 interrupt( ) 方法中断,因此必须包含在 try{} 代码块中。
class MyThread extends Thread {
public void run() {
int i = 0;
while (true) {
System.out.printf("%s=%d\n", getName(), ++i);
if (i > 100) break;
try
{
sleep(0);
}
catch (InterruptedException e)
{
}
}
}
}
public class program {
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
public void run() {
int i = 0;
while (true) {
System.out.printf("%s=%d\n", getName(), ++i);
if (i > 100) break;
try
{
sleep(0);
}
catch (InterruptedException e)
{
}
}
}
}
public class program {
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
3. 优先级
Java 使用 setPriority( ) 方法调整优先级。
4. 背景线程
Java 使用 setDaemon() 方法。在 Java 中一般将背景线程称之为"守护线程(daemon thread)"。需要注意的是即便背景线程未结束,进程依然会终止。必须在线程启动前设置。
5. 线程等待
Java 中都使用 join/Join() 方法阻止调用线程,直到某个线程结束。不过 Java 里面情况还是要复杂一些。当某个线程处于 join 等待时,它可能会被 interrupt( ) 方法中断,因此也得放在 try{} 代码块中。
package tread;
class ThreadT extends Thread {
public boolean stopFlag = false;
public void run() {
int i = 0;
while (!stopFlag) {
System.out.println(++i);
yield();
}
System.out.println("MyThread over");
}
}
class JoinThread extends Thread {
public void run() {
ThreadT my = new ThreadT();
my.start();
try {
my.join();
} catch (InterruptedException e) {
my.stopFlag = true;
System.out.println("JoinThread InterruptedException");
}
}
}
public class JoinTest {
public static void main(String[] args) {
JoinThread join = new JoinThread();
join.start();
try {
join.sleep(100*5);
join.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadT extends Thread {
public boolean stopFlag = false;
public void run() {
int i = 0;
while (!stopFlag) {
System.out.println(++i);
yield();
}
System.out.println("MyThread over");
}
}
class JoinThread extends Thread {
public void run() {
ThreadT my = new ThreadT();
my.start();
try {
my.join();
} catch (InterruptedException e) {
my.stopFlag = true;
System.out.println("JoinThread InterruptedException");
}
}
}
public class JoinTest {
public static void main(String[] args) {
JoinThread join = new JoinThread();
join.start();
try {
join.sleep(100*5);
join.interrupt();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
。。。。。。
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
JoinThread InterruptedException
MyThread over
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
JoinThread InterruptedException
MyThread over
6. 资源锁定
Java 提供了 synchronized 关键字用来解决多线程资源共享锁定的问题,synchronized 可用于方法或者某个代码段。
package tread;
class Res {
String lock="";
synchronized static void test() {
for (int i = 0; i < 10; i++) {
System.out.println( Thread.currentThread().getName()+"========="+ i);
Thread.yield();
}
}
void test2() {
synchronized(lock){
for (int i = 0; i < 10; i++) {
System.out.println( Thread.currentThread().getName()+"========"+ i);
Thread.yield(); //没有放弃锁
}
}
}
}
class MyThreadA extends Thread {
public void run() {
new Res().test2();
}
}
public class SynTest {
public static void main(String[] args){
MyThreadA t1 = new MyThreadA();
MyThreadA t2 = new MyThreadA();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
class Res {
String lock="";
synchronized static void test() {
for (int i = 0; i < 10; i++) {
System.out.println( Thread.currentThread().getName()+"========="+ i);
Thread.yield();
}
}
void test2() {
synchronized(lock){
for (int i = 0; i < 10; i++) {
System.out.println( Thread.currentThread().getName()+"========"+ i);
Thread.yield(); //没有放弃锁
}
}
}
}
class MyThreadA extends Thread {
public void run() {
new Res().test2();
}
}
public class SynTest {
public static void main(String[] args){
MyThreadA t1 = new MyThreadA();
MyThreadA t2 = new MyThreadA();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
当我们取消 synchronized 关键字时,我们会发现 t1 和 t2 交替执行。添加 synchronized 关键字以后,t2 会在 t1 执行完成后执行,因此对于方法的锁定是成功的。
结果:
t1========0
。。。。
t1========7
t1========8
t1========9
t2========0
。。。
t2========8
t2========9
。。。。
t1========7
t1========8
t1========9
t2========0
。。。
t2========8
t2========9
另外,需要注意的是 synchronized 是全局锁定,也就是说对于静态方法而言,它们会锁定全部有此标记的方法(尽管它们不是同一个方法);而对于对象实例,它们会锁定同一对象全部有此标记的方法(尽管它们不是同一个方法)。
7. 原子操作
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行倒结束,中间不会有任何线程切换操作。
通常所说的Java原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。不过如果你在long或double前面加了volatile,那么它就肯定是原子操作了。
看下面的例子,虽然 return i是一个原子操作,但是从获得i的值到方法返回之间有可能被其他线程修改,因此不要主观的认为下面的操作是原子操作。我们还是有必要为其添加同步关键字synchronized。另外 ++i 和 --i 都不是原子操作,它们都涉及读和写两个步骤。
public int getValue() { return i; }
《Thinking in Java》中关于最安全的做法提出了如下的方针,不过我不完全赞同。
- 如果你要synchronize类的一个方法,索性把所有的方法全都synchronize了。要判断,哪个方法该synchronize,哪个方法可以不synchronize,通常是很难的,而且也没什么把握。
- 删除synchronized的时候要绝对小心。通常这么做是为了性能,但是synchronized的开销在JDK1.3和1.4里已经大为降低了。此外,只有在用profiler分析过,确认synchronized确实是瓶颈的前提下才能这么作。
8. 线程协同
Java用对象锁来控制多线程安全,而所谓多线程协同无非是利用某种锁机制让线程暂时停顿下来,直到某个“信号”发生。为此,Java 将用来进行多线程协同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。
调用 wait() / notify() / notifyAll() 之前必须先获取其对象锁,否则会抛出"IllegalMonitorStateException - current thread not owner" 异常。
package tread;
class MyThread extends Thread {
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.println(i);
try
{
if (i == 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class SynTest2 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
while (t1.getState() != Thread.State.WAITING)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
System.out.println("Notify Thread");
synchronized(MyThread.o)
{
MyThread.o.notify();
}
}
}
class MyThread extends Thread {
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.println(i);
try
{
if (i == 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class SynTest2 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
while (t1.getState() != Thread.State.WAITING)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
System.out.println("Notify Thread");
synchronized(MyThread.o)
{
MyThread.o.notify();
}
}
}
输出
1
2
3
4
5
Notify Thread...
6
7
8
9
在上面这个例子中,当 i 等于 5 时,我们调用了对象锁 o 的 wait 方法阻塞线程。在 main 方法中我们循环检查对象状态,并适时发出信号结束等待。这个例子虽然很简单,但是由此可以处理多个线程之间的协同关系。当然,我们还可以给 wait 方法加一个参数,让其等待特定的时间,而不是上面例子中的无限制等待。
需要注意的是 wait() 会释放对象锁,sleep() 则不会。接着看下面的例子。
class MyThread extends Thread {
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.printf("%s - %d\n", Thread.currentThread().getName(), i);
try
{
if (i >= 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class Program {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.printf("%s - %d\n", Thread.currentThread().getName(), i);
try
{
if (i >= 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class Program {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
输出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5
9. 停止/中断线程
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
Java 不推荐使用 stop() / about() / suspend() / resume() 之类的方法来停止线程。原因包括:
- 在调用这些方法的时候,我们不能确定线程方法执行到何处,是否完成了特定的逻辑。
- 这些方法会让线程无法正确释放对象锁,可能造成死锁。
- 使用旗标(flag)。
- 调用 interrupt()。
class MyThread extends Thread {
public boolean stopFlag = false;
public void run() {
while (!stopFlag) {
try
{
System.out.println("Thread running, " + System.currentTimeMillis());
Thread.sleep(50);
}
catch (Exception e)
{
}
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
Thread.sleep(100);
}
catch (Exception e)
{
}
t.stopFlag = true;
}
}
public boolean stopFlag = false;
public void run() {
while (!stopFlag) {
try
{
System.out.println("Thread running, " + System.currentTimeMillis());
Thread.sleep(50);
}
catch (Exception e)
{
}
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
Thread.sleep(100);
}
catch (Exception e)
{
}
t.stopFlag = true;
}
}
当线程因某种原因处于阻塞等待状态时,我们就无法使用旗标终止它,那么改用interrupt()好了。
class MyThread extends Thread {
public void run() {
try
{
while (true) {
System.out.println("Thread running, " + System.currentTimeMillis());
synchronized (this) {
wait();
}
}
}
catch (Exception e)
{
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
while (t.getState() != Thread.State.WAITING) {
Thread.sleep(500);
}
t.interrupt();
}
catch (Exception e)
{
}
}
}
public void run() {
try
{
while (true) {
System.out.println("Thread running, " + System.currentTimeMillis());
synchronized (this) {
wait();
}
}
}
catch (Exception e)
{
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
while (t.getState() != Thread.State.WAITING) {
Thread.sleep(500);
}
t.interrupt();
}
catch (Exception e)
{
}
}
}
相关推荐
### Java线程学习知识点 #### 一、Java线程概览 - **定义与作用**:线程是在程序中独立且并发执行的路径。在Java中,线程被设计为语言的一部分,而不是作为操作系统的底层工具。每个Java程序至少包含一个主线程,在...
在Java编程语言中,线程是程序执行的基本单元,它允许程序并发地...同时,"Java线程学习和总结.files"目录下的文件可能是与文章相关的辅助资料,例如代码示例或图片。建议结合这些资料一起学习,以获得更全面的知识。
本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...
Java 线程学习笔记 Java 线程创建有两种方法: 1. 继承 Thread 类,重写 run 方法:通过继承 Thread 类并重写 run 方法来创建线程,这种方法可以使线程具有自己的执行逻辑。 2. 实现 Runnable 接口:通过实现 ...
### Java线程学习:深入理解线程生命周期与同步机制 #### 标题解析:Java线程学习 在Java编程语言中,线程是程序执行的基本单位之一,它允许并发执行多个任务,从而提高应用程序的效率和响应性。通过Java线程的...
### Java线程学习教程知识点详解 #### 一、教程概览 - **适用人群**: 本教程主要面向那些已经熟练掌握了Java语言基本语法和应用,但对于多线程和并发编程经验较少的Java开发者。 - **目标**: 学习者通过本教程的...
在"java线程学习一4"这个主题中,我们将会深入探讨Java中的多线程概念,以及如何通过源码理解和使用相关工具进行线程管理。 首先,我们需要理解Java中的线程创建方式。Java提供了两种主要的线程创建方法:继承...
通过学习以上知识点,初学者可以对Java线程有基本的理解,逐步掌握并发编程的核心技能。实践中的线程例子将有助于巩固理论知识,加深对线程工作原理的把握。在学习过程中,不断编写和调试多线程程序,是提升技能的...
本文将深入探讨Java线程的学习要点,帮助你提升在多线程环境下的编程能力。 首先,我们要明白线程的基本概念。线程是操作系统分配CPU时间的基本单元,一个进程中可以有多个线程同时执行任务,这使得程序可以在同一...
本专题资料聚焦于Java线程的学习,包括从基础到进阶的各种知识点。 1. **线程创建方式**:Java提供了两种创建线程的方式,一是通过继承Thread类,重写run()方法;二是实现Runnable接口,然后创建Thread对象并传入...
总的来说,Java多线程学习涵盖了线程的创建、同步、通信、调度以及异常处理等多个方面,深入理解和掌握这些知识点对于提升Java程序的性能和复杂性至关重要。通过阅读提供的"Java多线程.pdf"文档,你可以进一步了解和...
java中多线程下载学习,又新增了断点的实现,可以实现暂停继续下载网络文件的功能
Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java多线程学习Java...
对于学习java线程的初学者是很有用的资料,让自己很快的上手,帮助自己学习很快的知识。。。