import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 功能:线程安全的计数器,5个线程同时跑,计数到1000,输出线程名和计数
* @author Yolanda
*
*/
public class MySafeThread implements Runnable{
private static AtomicInteger count = new AtomicInteger(0);//线程安全的计数变量
private int threadCount = 0;//线程编号
private static int num = 1;
/**
* 功能:计数
*/
public static void calc(){
while((count.get())<1000)
{
count.incrementAndGet();//自增1,返回更新值
System.out.println("正在运行是线程" + Thread.currentThread().getName() + ":" + count);
}
}
/**
* 功能:线程运行方法,每次只能一个线程访问
*/
public synchronized void run() {
while(true)
{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
MySafeThread.calc();
}
}
public static void main(String[] args) {
//创建五个线程实例并启动
for (int i = 1; i < 6; i++) {
Thread mySafeThread = new Thread(new MySafeThread());
mySafeThread.start();
}
}
}
正在运行是线程Thread-1:2
正在运行是线程Thread-4:4
正在运行是线程Thread-0:4
正在运行是线程Thread-3:2
正在运行是线程Thread-3:9
正在运行是线程Thread-3:10
正在运行是线程Thread-0:8
正在运行是线程Thread-4:7
正在运行是线程Thread-4:13
正在运行是线程Thread-4:14
正在运行是线程Thread-4:15
正在运行是线程Thread-4:16
正在运行是线程Thread-4:17
正在运行是线程Thread-4:18
正在运行是线程Thread-4:19
正在运行是线程Thread-4:20
正在运行是线程Thread-4:988
正在运行是线程Thread-4:989
正在运行是线程Thread-4:990
正在运行是线程Thread-4:991
正在运行是线程Thread-4:992
正在运行是线程Thread-4:993
正在运行是线程Thread-4:994
正在运行是线程Thread-4:995
正在运行是线程Thread-4:996
正在运行是线程Thread-4:997
正在运行是线程Thread-4:998
正在运行是线程Thread-4:999
正在运行是线程Thread-4:1000
正在运行是线程Thread-2:399
正在运行是线程Thread-1:398
正在运行是线程Thread-0:700
正在运行是线程Thread-3:402
===========================我是更新线==============================
看上面的结果其实是有一点不对的,前面的几个计数发生了重复,这是由于锁的位置不正确导致的。所以对这个程序重写了一下。
package com.yolanda.fun.thread;
import java.util.concurrent.atomic.AtomicInteger;
public class MySafeCalcThread1 implements Runnable {
private static AtomicInteger count = new AtomicInteger(0);
public synchronized static void calc() {
if ((count.get()) < 1000) {
int c = count.incrementAndGet();// 自增1,返回更新值
System.out.println("正在运行是线程" + Thread.currentThread().getName() + ":" + c);
}
}
public void run() {
while(true)
{
MySafeCalcThread1.calc();
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
MySafeCalcThread1 thread = new MySafeCalcThread1();
Thread t = new Thread(thread);
t.start();
}
}
}
然后结果就对啦,就没有重复计数的问题啦。
结果:
正在运行是线程Thread-0:1
正在运行是线程Thread-1:2
正在运行是线程Thread-0:4
正在运行是线程Thread-2:3
正在运行是线程Thread-2:7
正在运行是线程Thread-0:6
正在运行是线程Thread-0:9
正在运行是线程Thread-1:5
正在运行是线程Thread-0:12
正在运行是线程Thread-5:11
正在运行是线程Thread-3:10
正在运行是线程Thread-2:8
.......
正在运行是线程Thread-4:996
正在运行是线程Thread-4:997
正在运行是线程Thread-4:998
正在运行是线程Thread-4:999
正在运行是线程Thread-4:1000
正在运行是线程Thread-1:743
正在运行是线程Thread-3:742
正在运行是线程Thread-0:935
正在运行是线程Thread-5:884
正在运行是线程Thread-2:747
还有一种写法是这样子的~
package com.yolanda.fun.thread;
import java.util.concurrent.atomic.AtomicInteger;
public class MySafeCalcThread2 implements Runnable {
private static AtomicInteger count = new AtomicInteger(0);
public static void calc() {
while ((count.get()) < 1000) {
int c = 0;
synchronized (count) {
if ((count.get()) < 1000) {
c = count.incrementAndGet();// 自增1,返回更新值
}
}
System.out.println("正在运行是线程" + Thread.currentThread().getName() + ":" + c);
}
}
public void run() {
while(true)
{
MySafeCalcThread2.calc();
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
MySafeCalcThread2 thread = new MySafeCalcThread2();
Thread t = new Thread(thread);
t.start();
}
}
}
正在运行是线程Thread-0:1
正在运行是线程Thread-3:2
正在运行是线程Thread-4:3
正在运行是线程Thread-5:4
正在运行是线程Thread-0:5
正在运行是线程Thread-0:6
正在运行是线程Thread-2:7
正在运行是线程Thread-2:8
正在运行是线程Thread-1:9
正在运行是线程Thread-2:10
........
正在运行是线程Thread-2:990
正在运行是线程Thread-2:991
正在运行是线程Thread-2:992
正在运行是线程Thread-1:993
正在运行是线程Thread-2:994
正在运行是线程Thread-0:995
正在运行是线程Thread-5:996
正在运行是线程Thread-5:997
正在运行是线程Thread-5:998
正在运行是线程Thread-5:999
正在运行是线程Thread-5:1000
============================我是更新线===============================
在此处补充一下JAVA多线程实现的两种方式
第一种:继承Thread类
package com.yolanda.fun.thread;
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程" + Thread.currentThread().getName() + "在运行");
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("线程" + Thread.currentThread().getName() + "在运行");// 线程main
}
}
}
线程main在运行
线程Thread-0在运行
线程main在运行
线程main在运行
线程Thread-0在运行
线程Thread-0在运行
线程main在运行
线程Thread-0在运行
线程main在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
结果分析:这里可以看到main线程和Thread-0线程交替运行。
所谓的多线程,指的是两个线程的代码可以同时运行,而不必一个线程需要等待另一个线程内的代码执行完才可以运行。对于单核CPU来说,是无法做到真正的多线程的,每个时间点上,CPU都会执行特定的代码,由于CPU执行代码时间很快,所以两个线程的代码交替执行看起来像是同时执行的一样。那具体执行某段代码多少时间,就和分时机制系统有关了。分时系统把CPU时间划分为多个时间片,操作系统以时间片为单位片为单位各个线程的代码,越好的CPU分出的时间片越小。所以看不到明显效果也很正常,一个线程打印5句话本来就很快,可能在分出的时间片内就执行完成了。所以,最简单的解决办法就是把for循环的值调大一点就可以了(也可以在for循环里加Thread.sleep方法)。
第二种:实现Runnable接口。和继承自Thread类差不多,不过实现Runnable后,还是要通过一个Thread来启动
package com.yolanda.fun.thread;
public class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程" + Thread.currentThread().getName() + "在运行");
}
}
public static void main(String[] args) {
MyRunnable thread = new MyRunnable();
Thread t = new Thread(thread);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("线程" + Thread.currentThread().getName() + "在运行");
}
}
}
线程main在运行
线程Thread-0在运行
线程main在运行
线程Thread-0在运行
线程main在运行
线程Thread-0在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
线程main在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
线程Thread-0在运行
两个线程也是交替运行。
其实Thread类也是实现的Runnable接口。
两种实现方式对比的关键就在于extends和implements的对比,当然是后者好。因为第一,继承只能但继承,实现可以多实现;第二,实现的方式对比继承的方式,也有利于减小程序之间的耦合。
因此,多线程的实现几乎都是使用的Runnable接口的方式。
线程状态
虚拟机中的线程状态有六种,定义在Thread.State中:
1、新建状态NEW
new了但是没有启动的线程的状态。比如"Thread t = new Thread()",t就是一个处于NEW状态的线程
2、可运行状态RUNNABLE
new出来线程,调用start()方法即处于RUNNABLE状态了。处于RUNNABLE状态的线程可能正在Java虚拟机中运行,也可能正在等待处理器的资源,因为一个线程必须获得CPU的资源后,才可以运行其run()方法中的内容,否则排队等待
3、阻塞BLOCKED
如果某一线程正在等待监视器锁,以便进入一个同步的块/方法,那么这个线程的状态就是阻塞BLOCKED
4、等待WAITING
某一线程因为调用不带超时的Object的wait()方法、不带超时的Thread的join()方法、LockSupport的park()方法,就会处于等待WAITING状态
5、超时等待TIMED_WAITING
某一线程因为调用带有指定正等待时间的Object的wait()方法、Thread的join()方法、Thread的sleep()方法、LockSupport的parkNanos()方法、LockSupport的parkUntil()方法,就会处于超时等待TIMED_WAITING状态
6、终止状态TERMINATED
线程调用终止或者run()方法执行结束后,线程即处于终止状态。处于终止状态的线程不具备继续运行的能力。